Como hemos visto, las series de tiempo pueden presentar varios patrones, y comúnmente es útil dividir o descomponer la serie en cada uno de esos patrones. Recordando, una serie de tiempo puede exhibir:

  • Tendencia
  • Estacionalidad
  • Ciclos

Al realizar la descomposición de la serie, usualmente se juntan el patrón cíclico y la tendencia en uno solo, llamado simplemente tendencia. Así, tendríamos tres componentes en una serie de tiempo cualquiera:

  1. Tendencia
  2. Estacionalidad
  3. Residuo (lo que no es parte ni de la tendencia, ni del efecto estacional)

Transformaciones y ajustes

Ahora, lo que buscamos es analizar datos lo más sencillos posibles, por lo que en ocasiones vale la pena llevar a cabo transformaciones o ajustes de la serie de tiempo. Vamos a revisar cuatro tipos: ajustes de calendario, por población, inflacionarios y transformaciones matemáticas.

Ajustes de calendario

Parte de la variación en las series de tiempo puede deberse a efectos muy sencillos que tienen que ver con el calendario. Algunos ejemplos de esto pueden ser variaciones en datos mensuales, debido a la cantidad de días hábiles en cada mes. Tomemos el caso del volumen de transacciones de Alphabet Inc., holding de Google, entre otras.

library("easypackages")
packages("tidyverse", "tidyquant", "lubridate", "patchwork", "fpp2","fpp3","scales", "timetk")

transacciones_mensuales <- tq_get("GOOG", get = "stock.prices",
                                  from = "2015-01-01") %>%
  summarise_by_time(
    .date_var      =  date, 
    .by            = "month",
    monthly_volume = sum(volume),
    trading_days   = n(),
    mean_volume    = mean(volume)) %>%
  mutate(month     = yearmonth(date)) %>% 
  select(month, everything(), -date) %>% 
  as_tsibble(index = month)

transacciones_mensuales

# en vez de "summarise_by_time" se pudo haber hecho esto:

# group_by(month = floor_date(date, "month")) %>%
#   summarise(monthly_volume = sum(volume),
#             trading_days = n(),
#             mean_volume = mean(volume)) %>%
#   mutate(month = yearmonth(date)) %>% select(month,-date, monthly_volume:mean_volume) %>% 
#   as_tsibble(index = month)

Podemos graficar ambas con dos (o más) alternativas: cada variable por separado y unirlas con patchwork, o utilizar pivot_longer() y facet_wrap().

# Opción 1
p1 <- ggplot(data = transacciones_mensuales) + 
  geom_line(aes(x = month, y = monthly_volume)) +
  ylab("Vol. total mensual") + 
  xlab("")

p2 <- ggplot(data = transacciones_mensuales) + 
  geom_line(aes(x = month, y = mean_volume)) +
  ylab("Vol. promedio diario") +
  xlab("")

p1 / p2

# Opción 2

transacciones_mensuales %>% 
  pivot_longer(cols = c(monthly_volume, mean_volume),
               names_to = "variable", values_to = "valor") %>% 
  ggplot(aes(x = month, y = valor)) +
  geom_line() + ylab("Transacciones") + xlab("") +
  facet_wrap(~ variable, ncol = 1, scales = "free_y")

p <- global_economy %>% 
  filter(Country == "Mexico") %>% 
  pivot_longer(cols = -c(Country:Year)) %>% 
  ggplot(aes(x = Year, y = value)) +
  geom_line() + facet_wrap(~ name, scales = "free_y")

plotly::ggplotly(p)
`group_by_()` is deprecated as of dplyr 0.7.0.
Please use `group_by()` instead.
See vignette('programming') for more help
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.

La serie original tomando el total de transacciones mensuales presenta mayor variación que cuando ajustamos la serie por los dias calendario (llegando al volumen promedio diario).

Ajustes poblacionales

Cualquier variable que se ve afectada por la población, puede ser expresada en términos per cápita. Si, p. ej., con la pandemia del COVID-19, se quisiera analizar la cantidad de médicos laborando en México y ver su evolución histórica, tomar únicamente la cantidad de médicos podría ser engañoso, si no se toma en consideración el crecimiento de la población. Así, sería más recomendable observar la variable de cantidad de médicos por cada 100 mil habitantes, por ejemplo, para ver si la cantidad de médicos ha aumentado, se ha mantenido estable o ha disminuido a lo largo del tiempo.

Otro ejemplo que se puede analizar es el del PIB de los países. Tomemos tres casos: México, Australia e Islandia. Si consideramos el PIB para ver qué tan bien se ha comportado la economía de cada uno de los países, veríamos el siguiente efecto:

glimpse(global_economy)
Rows: 15,150
Columns: 9
Key: Country [263]
$ Country    <fct> Afghanistan, Afghanistan, Afg...
$ Code       <fct> AFG, AFG, AFG, AFG, AFG, AFG,...
$ Year       <dbl> 1960, 1961, 1962, 1963, 1964,...
$ GDP        <dbl> 537777811, 548888896, 5466666...
$ Growth     <dbl> NA, NA, NA, NA, NA, NA, NA, N...
$ CPI        <dbl> NA, NA, NA, NA, NA, NA, NA, N...
$ Imports    <dbl> 7.024793, 8.097166, 9.349593,...
$ Exports    <dbl> 4.132233, 4.453443, 4.878051,...
$ Population <dbl> 8996351, 9166764, 9345868, 95...
ge <- global_economy %>% 
  filter(Country == "Mexico" | Country == "Iceland" | Country == "Australia")
  # filter(Country %in% c("Mexico", "Iceland", "Australia"))

p3 <- ggplot(ge) + aes(x = Year, y = GDP, color = Country) + 
  geom_line()
 
p3

¿Realmente creen que la economía de México esté a la par de la economía de Australia e, incluso, muy superior a la de Islandia?

Lo cierto es que no. La variable del PIB no está considerando la población de cada país; México tiene una población mucho mayor que la de los otros dos países:

ggplot(ge) + aes(x = Year, y = Population, color = Country) +
  geom_line()

Ahora, si ajustamos los datos del PIB para tomar en cuenta la población, obtendríamos el PIB per cápita.

p4 <- ggplot(ge) + aes(x = Year, y = GDP / Population, color = Country) +
  geom_line() + ylab("GDP per capita")

p4

Aquí queda claro que la economía de Australia e Islandia es bastante superior a la mexicana (lo producido en México por persona es mucho menor que lo producido por persona en Australia e Islandia).

Ajustes inflacionarios

Los datos afectados por el valor del dinero en el tiempo se deben ajustar antes de modelarse. Esto se debe a la inflación: sus abuelos podían comprar una coca y un gansito tal vez con menos de 3 pesos. ¿Cuánto dinero necesitarían hoy para comprar una coca y un gansito? Entonces, las series de tiempo financieras se ajustan a la inflación, y se expresan en términos de alguna moneda constante (en el tiempo). Por ejemplo, se puede medir el valor de la vivienda en Gudalajara, con precios constantes de 2010 (imaginando que no hubiera cambiado el valor del dinero en el tiempo).

Para realizar el ajuste inflacionario, se toma un índice de precios. Si \(z_t\) es el índice de precios y \(y_t\) es el valor original de la vivienda en el tiempo \(t\), entonces el precio de la vivienda, \(x_t\), ajustado a valor del año 2010, estaría dado por:

\[ x_t = \frac{y_t}{z_t} * z_{2010} \] Los índices de precios son construidos, generalmente, por el gobierno, o alguna dependencia de gobierno. En México, el más común es el Índice Nacional de Precios al Consumidor (INPC), que es construido por el INEGI.

Veamos el caso de la industria de periódicos y libros impresos en Australia y su crecimiento o decrecimiento en el tiempo. Tomaremos los datos de aus_retail y ajustaremos la inflación con el índice de precios al consumidor, CPI, dentro de la tabla global_economy.

print_retail <- aus_retail %>%
  filter(Industry == "Newspaper and book retailing") %>%
  group_by(Industry) %>%
  index_by(Year = year(Month)) %>%
  summarise(Turnover = sum(Turnover))

autoplot(print_retail)
Plot variable not specified, automatically selected `.vars = Turnover`

aus_economy <- global_economy %>%
  filter(Code == "AUS")
  # filter(Country == "Australia")

print_retail %>% 
  # unir las tablas print_retail y aus_economy con base en
  # print_retail
  left_join(aus_economy, by = "Year") %>%
  # calculando las ventas sin inflación
  mutate(Adjusted_turnover = Turnover / CPI) %>%
  #
  pivot_longer(
    cols            = c(Turnover, Adjusted_turnover),
    names_to        = "Type",
    values_to       = "Turnover",
    names_transform = list(Type = as_factor) 
  ) %>% 
  # gather es la versión vieja de pivot_longer, por lo tanto ya
  # no se recomienda utilizar
  # gather("Type", "Turnover", Turnover, Adjusted_turnover, factor_key = TRUE) %>%
  ggplot(aes(x = Year, y = Turnover)) +
    geom_line() +
    facet_grid(vars(Type), scales = "free_y") +
    xlab("Years") + ylab(NULL) +
    ggtitle("Turnover for the Australian print media industry")

Al ver los datos ajustados, nos percatamos de que la venta de estos productos impresos ha decaído mucho más de lo que los datos sin ajustar sugieren.

Transformaciones matemáticas

Si los datos presentan variación que aumenta o disminuye con el nivel de la serie, se puede sugerir una transformación matemática. Las transformaciones logarítmicas son muy utilizadas. Una de las razones es porque son fácilmente interpretables: los cambios en el valor logarítmico son relativos (o porcentuales), a cambios en la escala original. Una desventaja de la transformación logarítmica, es que no se puede utilizar con valores iguales a cero o negativos.

Recordando clases anteriores, donde teníamos las ventas trimestrales de J&J. Se observa que la variación aumenta con el nivel de la serie. Asimismo, parece tener una forma exponencial.

data("JohnsonJohnson")
autoplot(JohnsonJohnson)+
  ggtitle("Ventas trimestrales de J&J")

Podemos probar aplicando una transformación logarítmica a los datos para ver si logramos hacer que la variación sea uniforme a lo largo del tiempo, y ver si podemos hacer que parezca tener una tendencia lineal.

autoplot(log(JohnsonJohnson)) +
  ggtitle("Logaritmo de las ventas trim. de J&J")

Otro tipo de transformaciones matemáticas son las transformaciones de potencia. P. ej., sacar la raíz cuadrada o cúbica de los datos, etc. Estas transformaciones se pueden escribir como \(w_t = y_t^p\).

autoplot(JohnsonJohnson^(1/3)) +
  ggtitle("Logaritmo de las ventas trim. de J&J")

Una familia de transformaciones matemáticas que incluye, tanto logaritmos, como potencias son las transformaciones Box-Cox, que dependen de un parámetro, \(\lambda\) y se definen de la siguiente manera:

\[w_{t}=\left\{\begin{array}{ll} \log \left(y_{t}\right) & \text { si } \lambda=0 \\ \left(y_{t}^{\lambda}-1\right) / \lambda & \text { en otro caso } \end{array}\right.\]

Aquí, el logaritmo siempre es un logaritmo natural. Si \(\lambda = 0\), se utiliza un logaritmo natural, de lo contrario se utiliza una potencia, con un escalado simple.

Si \(\lambda = 1\) entonces \(w_t = y_t-1\), lo que significaría que los datos solo se desplazarían hacia abajo, sin cambiar la forma de la serie de tiempo. Para cualquier otro valor de \(\lambda\), la serie cambiará de forma.

¿Cómo escoger el valor de \(\lambda\) a utilizar?

Un buen valor de \(\lambda\) es aquel que hace que el tamaño de la variación estacional sea el mismo a lo largo de la serie, para que el modelado y pronóstico sea más sencillo.

Para ejemplificar esto, tomemos datos sobre la producción de gas en Australia.

p5a <- aus_production %>% autoplot(Gas)+ 
  ggtitle("Producción de gas (datos reales)")

p5 <- aus_production %>% autoplot(box_cox(Gas,lambda = -0.5)) + ggtitle("Box-Cox, lambda = -0.5")

p6 <- aus_production %>% autoplot(box_cox(Gas,lambda = 0)) + ggtitle("Box-Cox, lambda = 0 (log)")

p7 <- aus_production %>% autoplot(box_cox(Gas,lambda = 0.1)) + ggtitle("Box-Cox, lambda = 0.1")

p8 <- aus_production %>% autoplot(box_cox(Gas,lambda = 1)) + ggtitle("Box-Cox, lambda = 1")

p5a


(p5 | p6) / (p7 | p8)

La característica de guerrero nos ayuda a seleccionar un valor de \(\lambda\). En este caso, escogió un \(\lambda = 0.12\)

(lambda <- aus_production %>%
  features(Gas, features = guerrero) %>%
  pull(lambda_guerrero))
[1] 0.1204864
aus_production %>% autoplot(box_cox(Gas, lambda))

Componentes de las series de tiempo

Si empleamos una descomposición aditiva, se podría explicar a nuestra serie de tiempo, \(y_t\) como:

\[ y_t = S_t + T_t + R_t \]

Donde \(S_t\) representa el componente estacional, \(T_t\) la tendencia-ciclo y \(R_t\) el residuo. Una descomposición multiplicativa tomaría la forma:

\[ y_t = S_t * T_t * R_t \]

¿Cuándo utilizar la descomposición aditiva y cuándo la multiplicativa?

La aditiva es mejor cuando la magnitud de la fluctuación estacional no cambia con el nivel de la serie. Por el contrario, cuando la variación del componente estacional cambia a lo largo del tiempo, se recomendaría utilizar la multiplicativa. Esta última es muy común en series de tiempo económicas.

Una alternativa para no utilizar la descomposición multiplicativa es transformar los datos para evitar la fluctuación en la variación y posteriormente aplicar la aditiva. De hecho, si se aplica una transformación logarítmica a los datos, es equivalente a utilizar una descomposición multiplicativa, porque:

\[y_{t}=S_{t} \times T_{t} \times R_{t} \quad \text { es equivalente a } \quad \log y_{t}=\log S_{t}+\log T_{t}+\log R_{t}\] Veamos el ejemplo del empleo en el sector de las ventas al menudeo en EEUU desde 1990.

us_retail_employment <- us_employment %>%
  filter(year(Month) >= 1990, Title == "Retail Trade") %>%
  select(-Series_ID)

us_retail_employment %>%
  autoplot(Employed) +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

Vamos a llevar a cabo un tipo de descomposición llamado STL (que analizaremos con mayor detalle posteriormente).

dcmp <- us_retail_employment %>%
  model(STL(Employed))

components(dcmp)

Estos comandos llevan a cabo la descomposición de la serie, como se puede ver en la tabla. La tendencia, trend muestra el movimiento de la serie, sin considerar las fluctuaciones estacionales ni el residuo. Podemos analizar la tendencia de la serie gráficamente:

us_retail_employment %>%
  autoplot(Employed, color='gray') +
  autolayer(components(dcmp), trend, color='red') +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

Podemos graficar los tres componentes simultáneamente con:

components(dcmp) %>% autoplot() + xlab("Year")

La gráfica nos indica que se llevó a cabo una descomposición STL, de forma aditiva, y nos grafica:

  1. La serie original.
  2. La tendencia.
  3. El componente estacional.
  4. El residuo.

Si se suma cada uno de los componentes, obtenemos la serie original.

Las barras grises en estas gráficas indican las escalas relativas de los componentes.

Datos desestacionalizados

En ocasiones, los datos presentados por el gobierno u otra organización se dice que están desestacionalizados. Esto significa que le quitaron el componente estacional a la serie. Si se quita, los datos ahora están “ajustados estacionalmente”. Para la descomposición aditiva, los datos destacionalizados están dados por \(y_t - S_t\), mientras que en la multiplicativa estarían dados por \(\frac{y_t}{S_t}\). Veamos los datos del empleo, desestacionalizados:

us_retail_employment %>%
  autoplot(Employed, color='gray') +
  autolayer(components(dcmp), season_adjust, color='blue') +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail")

Si la variación debido a la estacionalidad no es de interés en particular, los datos desestacionalizados pueden ser muy útiles. Un ejemplo común es el nivel de desempleo o crecimiento económico. El INEGI reporta las cifras de manera desestacionalizadas. Esto nos permite ver el estado de la economía, sin tomar en cuenta factores estacionales.

Medias móviles

La descomposición de series de tiempo clásica utilizaba medias móviles para definir el componente de tendencia.

Así, una suavización de media móvil de orden \(m\) estaría dada por:

\[ \hat{T}_{t}=\frac{1}{m} \sum_{j=-k}^{k} y_{t+j} \] en donde \(m = 2k +1\). Entonces, el estimado de de la tendencia en el periodo \(t\), \(\hat{T}_t\), se obtiene al promediar los valores de la serie de tiempo dentro de los \(k\) periodos alrededor de \(t\). A esto se le llama una media móvil de orden \(m\); \(m\)-MA.

p <- global_economy %>%
  filter(Country == "Mexico") %>%
  autoplot(Exports) +
  xlab("Year") + ylab("% of GDP") +
  ggtitle("Total Mexican exports")

plotly::ggplotly(p)

En la gráfica tenemos las exportaciones de México desde 1960 a 2017, como porcentaje del PIB. Podemos calcular una media móvil de orden 5; esto es, obtener el promedio de 5 periodos, para cada momento, \(t\), con el centro de la “ventana” en \(t\). Así, de acuerdo a la ecuación presentada, \(k = 2\) y \(m = 2k +1 = 5\).

mex_exports <- global_economy %>%
  filter(Country == "Mexico") %>%
  mutate(
    `5-MA` = slide_dbl(Exports, mean, .size = 5, .align = "center")
  )

Para ver cómo se presenta esta media móvil gráficamente, podemos hacer lo siguiente:

gg <- mex_exports %>%
  ggplot(aes(x = Year, y = Exports)) + 
  geom_line() +
  xlab("Year") + ylab("Exports (% of GDP)")
  
gg + geom_line(aes(y = `5-MA`), color='red') +
  ggtitle("Total Mexican exports & 5-MA")


# Se logra lo mismo con autoplot() y autolayer()
mex_exports %>% 
  autoplot(Exports) + 
  autolayer(mex_exports, `5-MA`, color = "red") +
  xlab("Year") + ylab("Exports (% of GDP)") +
  ggtitle("Total Mexican exports & 5-MA")

Se puede ver que la tendencia es mucho más suave que los datos originales, captura el movimiento principal de la serie, pero deja de lado las fluctuaciones intermedias o menores.

Qué tan suave esté la curva resultante, dependerá del orden de la media móvil.

mex_exports <- mex_exports %>%
  mutate(
    `1-MA` = slide_dbl(Exports, mean, .size = 1, .align = "center"),
    `3-MA` = slide_dbl(Exports, mean, .size = 3, .align = "center"),
    `7-MA` = slide_dbl(Exports, mean, .size = 7, .align = "center"),
    `9-MA` = slide_dbl(Exports, mean, .size = 9, .align = "center"),
    `11-MA` = slide_dbl(Exports, mean, .size = 11, .align = "center"),
    `15-MA` = slide_dbl(Exports, mean, .size = 15, .align = "center"),
    `17-MA` = slide_dbl(Exports, mean, .size = 17, .align = "center"),
    `21-MA` = slide_dbl(Exports, mean, .size = 21, .align = "center")
  )

gg <- mex_exports %>%
  ggplot(aes(x = Year, y = Exports)) + 
  geom_line() +
  xlab("Year") + ylab("Exports (% of GDP)")

g1 <- gg +
 geom_line(aes(y = `1-MA`), color='red') +
  ggtitle("1-MA")
g3 <- gg +
 geom_line(aes(y = `3-MA`), color='red') +
  ggtitle("3-MA")
g5 <- gg +
 geom_line(aes(y = `5-MA`), color='red') +
  ggtitle("5-MA")
g9 <- gg +
 geom_line(aes(y = `9-MA`), color='red') +
  ggtitle("9-MA")
g15 <- gg +
 geom_line(aes(y = `15-MA`), color='red') +
  ggtitle("15-MA")
g21 <- gg +
 geom_line(aes(y = `21-MA`), color='red') +
  ggtitle("21-MA")

(g1 | g3 | g5) /
  (g9 | g15 | g21)


# Graficando las 6 series de una misma vez utilizando facetas
mex_exports %>% 
  pivot_longer(
    cols      = `5-MA`:`21-MA`,
    names_to  = "Orden",
    values_to = "Media móvil"
  ) %>% 
  ggplot(aes(x = Year, y = Exports)) + 
  geom_line() + 
  geom_line(aes(y = `Media móvil`), color = "red") +
  xlab("Year") + ylab("Exports (% of GDP)") +
  facet_wrap(~ Orden)

Como se puede observar, un modelo 1-MA realmente no llevaría a cabo ninguna suavización, y un modelo 21-MA, en este caso, se convertiría prácticamente en una línea recta.

Medias móviles de medias móviles

A una suavización de media móvil se le puede aplicar una nueva suavización de media móvil. Por ejemplo, considerando la producción de cerveza, podemos obtener una media móvil de orden 4 y a eso sacarle la media móvil de orden 2:

beer <- aus_production %>%
  filter(year(Quarter) >= 1992) %>%
  select(Quarter, Beer)

beer_ma <- beer %>%
  mutate(
    `4-MA` = slide_dbl(Beer, mean, .size = 4, .align = "center-left"),
    `2x4-MA` = slide_dbl(`4-MA`, mean, .size = 2, .align = "center-right")
  )

beer_ma

Al obtener la media móvil de orden 4, 4-MA, lo que estamos haciendo es:

\[ \text{4-MA} = \hat{T}_t = \frac{1}{4}\left(y_{t-2}+y_{t-1}+y_{t}+y_{t+1}\right) \] y, al sacar la media móvil de orden 2 de esta media móvil, 2 \(\times\) 4 - MA, sería:

\[ 2 \times 4 \text{-MA} = \hat{T}_{t} = \frac{1}{2}\left[\frac{1}{4}\left(y_{t-2}+y_{t-1}+y_{t}+y_{t+1}\right)+\frac{1}{4}\left(y_{t-1}+y_{t}+y_{t+1}+y_{t+2}\right)\right] \] Simplificando, quedaría:

\[ 2 \times 4 \text{-MA} = \hat{T}_{t} = \frac{1}{8} y_{t-2}+\frac{1}{4} y_{t-1}+\frac{1}{4} y_{t}+\frac{1}{4} y_{t+1}+\frac{1}{8} y_{t+2} \] Así, llegamos a ver que la media móvil de una media móvil es simplemente una media móvil ponderada.

Medias móviles ponderadas

Como acabamos de ver, la combinación de dos o más medias móviles produce una media móvil ponderada. Esto es, una media móvil que depende en cierta proporción de cada rezago.

Una media móvil ponderada de orden \(m\) se puede escribir como:

\[ \hat{T}_{t} = \sum_{j=-k}^{k} a_{j} y_{t+j} \] donde \(k = (m - 1) / 2\) y los pesos o ponderaciones están dados por \(\left[a_{-k}, \dots, a_{k}\right]\). La suma de los pesos debe sumar 1.

Podemos decir, entonces, que el caso de la media móvil simple \(m\)-MA es un caso particular de la media móvil ponderada, donde todos sus pesos son iguales a \(1 / m\).

us_retail_employment_ma <- us_retail_employment %>%
  mutate(
    `12-MA` = slide_dbl(Employed, mean, .size = 12, .align = "cr"),
    `2x12-MA` = slide_dbl(`12-MA`, mean, .size = 2, .align = "cl")
  )

us_retail_employment_ma %>%
  autoplot(Employed, color='gray') +
  autolayer(us_retail_employment_ma, vars(`2x12-MA`), color='red') +
  xlab("Year") + ylab("Persons (thousands)") +
  ggtitle("Total employment in US retail, 2x12-MA")

\[ T_t = \frac{1}{2}\left(\frac{1}{12} ( y_{t-6} + ) \right) \]

Métodos de descomposición

Descomposición clásica

Hay dos tipos de descomposición clásica: aditiva y multiplicativa. En este tipo de descomposición, se asume que el componente estacional es constante a lo largo del tiempo.

us_retail_employment %>%
  model(classical_decomposition(Employed, type = "additive")) %>%
  components() %>%
  autoplot() + xlab("Year") +
  ggtitle("Classical additive decomposition of total US retail employment")

Hoy en día, ya no se recomienda utilizar el método clásico de descomposición, ya que existen diversos métodos mejores.

Algunas de las desventajas del método clásico son las siguientes:

  • La estimación del componente de tendencia no está disponible para los primeras y últimas observaciones.

  • El componente de tendencia suele suavizar de más incrementos o caídas rápidas en los datos.

  • Asume que el componente estacional se repite año con año, por lo que no captura cambios en el patrón estacional.

Descomposición X11

Este método funciona bastante bien con datos trimestrales y mensuales. Está basado en la descomposición clásica, pero incluye pasos adicionales para lidiar con los problemas de ella.

Así, X11 logra obtener estimadores para todos los puntos y el componente estacional puede variar ligeramente con el tiempo. También, cuenta con mecanismos para lidiar con variaciones por días calendario, efectos de días feriados, etc.

x11_dcmp <- us_retail_employment %>%
  model(x11 = feasts:::X11(Employed, type = "additive")) %>%
  components()

autoplot(x11_dcmp) + xlab("Year") +
  ggtitle("Additive X11 decomposition of US retail employment in the US")

Podemos utilizar gráficas estacionales o gráficas de sub-series estacionales para visualizar la variación en el componente estacional a lo largo del tiempo.

x11_dcmp %>% 
  gg_season()
Plot variable not specified, automatically selected `y = Employed`

x11_dcmp %>% 
  gg_subseries(seasonal)

Descomposición SEATS

“SEATS” son las siglas de “Seasonal Extraction Arima Time Series”. Este método solo funciona para datos trimestrales o mensuales. Por lo que, si se cuenta con datos de otra periodicidad, se debe implementar otro método.

seats_dcmp <- us_retail_employment %>%
  model(seats = feasts:::SEATS(Employed)) %>%
  components()
autoplot(seats_dcmp) + xlab("Year") +
  ggtitle("SEATS decomposition of total US retail employment")

Descomposición STL

STL significa “Seasonal and Trend decomposition using Loess” (“Loess” es un método de estimación de relaciones no lineales).

STL tiene varias ventajas sobre los otros métodos:

  • Puede tratar con cualquier tipo de estacionalidad, no solo mensual o trimestral.

  • El componente estacional puede variar con el tiempo y el usuario decide la magnitud del cambio.

  • La suavización del componente de tendencia también es controlado por el usuario.

  • Puede ser robusto ante outliers, para que observaciones inusuales no afecten el componente de tendencia.

Las desventajas de este método son que no controla de manera automática la variación debido a días hábiles o variaciones por calendario. También, solo permite hacer descomposiciones aditivas.

us_retail_employment %>%
  model(STL(Employed ~ trend(window=7) + season(window='periodic'),
    robust = TRUE)) %>%
  components() %>%
  autoplot()


# modificando la tendencia
us_retail_employment %>%
  model(STL(Employed ~ trend(window=15) + season(window='periodic'),
    robust = TRUE)) %>%
  components() %>%
  autoplot()


# modificando la estacionalidad
us_retail_employment %>%
  model(STL(Employed ~ trend(window=7) + season(window=21),
    robust = TRUE)) %>%
  components() %>%
  autoplot()

Esta gráfica muestra una descomposición mediante STL, ajustando algunos parámetros (el componente de tendencia es más flexible, el componente estacional es fijo y se agregó la opción de robustez). Como muestra el código, los dos parámetros principales a seleccionar al usar STL son la tendencia, trend(window = x) y la estacionalidad, season(window = y).

Estos parámetros controlan qué tan rápido cambian los componentes de tendencia y estacional, respectivamente. Valores más bajos provocan cambios más rápidos. NOTA: los valores escogidos de los parámetros deben ser impares. Si se desea mantener el mismo componente estacional a lo largo del tiempo, se debería definir como periódico, season(window = "periodic"), como en el caso anterior.

Tarea

  1. Tomando el PIB de cada país, GDP, contenido en la tabla global_economy, grafique el PIB per cápita a lo largo del tiempo. ¿Cómo ha sido la evolución de la economía de los países en el tiempo? ¿Cuál país tiene el mayor PIB per cápita?

  2. Grafique las siguientes series de tiempo y transfórmelas y/o ajústelas si lo considera necesario. ¿Qué efecto tuvo la transformación?

    1. PIB de EEUU, de global_economy.
    2. PIB de México, también de global_economy.
    3. Demanda de electricidad en el estado de Victoria (Australia), de vic_elec.
  3. ¿Es útil realizar una transformación de Box-Cox a los datos canadian_gas? ¿Por qué sí o por qué no?

  4. El dataset fma::plastics tiene información de las ventas mensuales (medidas en miles) del producto A para un productor de plásticos, a lo largo de cinco años.

    1. Grafique la serie de tiempo para el producto A. ¿Identifica algún componente de tendencia-ciclo y/o estacional?
    2. Utilice una descomposición clásica multiplicativa para calcular el componente de tendencia y estacional.
    3. ¿Los resultados coinciden con su respuesta al inciso i)?
    4. Calcule y grafique los datos desestacionalizados.
    5. Cambie, manualmente, una observación para que sea un outlier (p. ej., sume 500 a una observación). Vuelva a estimar los datos desestacionalizados. ¿Cuál fue el efecto de ese outlier?
    6. ¿Hace alguna diferencia que el outlier se encuentre cerca del final de la serie o más alrededor del centro?
LS0tDQp0aXRsZTogIkRlc2NvbXBvc2ljacOzbiBkZSBzZXJpZXMgZGUgdGllbXBvIg0KYXV0aG9yOiAiUGFibG8gQmVuYXZpZGVzIEhlcnJlcmEiDQpkYXRlOiAyMDIwLTAyLTI4DQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgZ2l0aHViX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgZGV2OiBqcGVnDQotLS0NCmBgYHtyIHNldHVwLCBlY2hvID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobz0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gNykNCmBgYA0KDQpDb21vIGhlbW9zIHZpc3RvLCBsYXMgc2VyaWVzIGRlIHRpZW1wbyBwdWVkZW4gcHJlc2VudGFyIHZhcmlvcyBwYXRyb25lcywgeSBjb23Dum5tZW50ZSBlcyDDunRpbCBkaXZpZGlyIG8gZGVzY29tcG9uZXIgbGEgc2VyaWUgZW4gY2FkYSB1bm8gZGUgZXNvcyBwYXRyb25lcy4gUmVjb3JkYW5kbywgdW5hIHNlcmllIGRlIHRpZW1wbyBwdWVkZSBleGhpYmlyOg0KDQoqIFRlbmRlbmNpYQ0KKiBFc3RhY2lvbmFsaWRhZA0KKiBDaWNsb3MNCg0KQWwgcmVhbGl6YXIgbGEgZGVzY29tcG9zaWNpw7NuIGRlIGxhIHNlcmllLCB1c3VhbG1lbnRlIHNlIGp1bnRhbiBlbCBwYXRyw7NuIGPDrWNsaWNvIHkgbGEgdGVuZGVuY2lhIGVuIHVubyBzb2xvLCBsbGFtYWRvIHNpbXBsZW1lbnRlICoqdGVuZGVuY2lhKiouIEFzw60sIHRlbmRyw61hbW9zIHRyZXMgY29tcG9uZW50ZXMgZW4gdW5hIHNlcmllIGRlIHRpZW1wbyBjdWFscXVpZXJhOg0KDQoxLiBUZW5kZW5jaWENCjIuIEVzdGFjaW9uYWxpZGFkDQozLiBSZXNpZHVvIChsbyBxdWUgbm8gZXMgcGFydGUgbmkgZGUgbGEgdGVuZGVuY2lhLCBuaSBkZWwgZWZlY3RvIGVzdGFjaW9uYWwpDQoNCiMgVHJhbnNmb3JtYWNpb25lcyB5IGFqdXN0ZXMNCg0KQWhvcmEsIGxvIHF1ZSBidXNjYW1vcyBlcyBhbmFsaXphciBkYXRvcyBsbyBtw6FzIHNlbmNpbGxvcyBwb3NpYmxlcywgcG9yIGxvIHF1ZSBlbiBvY2FzaW9uZXMgdmFsZSBsYSBwZW5hIGxsZXZhciBhIGNhYm8gKnRyYW5zZm9ybWFjaW9uZXMqIG8gKmFqdXN0ZXMqIGRlIGxhIHNlcmllIGRlIHRpZW1wby4gVmFtb3MgYSByZXZpc2FyIGN1YXRybyB0aXBvczogYWp1c3RlcyBkZSBjYWxlbmRhcmlvLCBwb3IgcG9ibGFjacOzbiwgaW5mbGFjaW9uYXJpb3MgeSB0cmFuc2Zvcm1hY2lvbmVzIG1hdGVtw6F0aWNhcy4NCg0KIyMjIEFqdXN0ZXMgZGUgY2FsZW5kYXJpbw0KDQpQYXJ0ZSBkZSBsYSB2YXJpYWNpw7NuIGVuIGxhcyBzZXJpZXMgZGUgdGllbXBvIHB1ZWRlIGRlYmVyc2UgYSBlZmVjdG9zIG11eSBzZW5jaWxsb3MgcXVlIHRpZW5lbiBxdWUgdmVyIGNvbiBlbCBjYWxlbmRhcmlvLiBBbGd1bm9zIGVqZW1wbG9zIGRlIGVzdG8gcHVlZGVuIHNlciB2YXJpYWNpb25lcyBlbiBkYXRvcyBtZW5zdWFsZXMsIGRlYmlkbyBhIGxhIGNhbnRpZGFkIGRlIGTDrWFzIGjDoWJpbGVzIGVuIGNhZGEgbWVzLiBUb21lbW9zIGVsIGNhc28gZGVsIHZvbHVtZW4gZGUgdHJhbnNhY2Npb25lcyBkZSBBbHBoYWJldCBJbmMuLCBob2xkaW5nIGRlIEdvb2dsZSwgZW50cmUgb3RyYXMuDQoNCmBgYHtyIGFqdXN0ZSBjYWxlbmRhcmlvIGdvb2dsZSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZScsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KCJlYXN5cGFja2FnZXMiKQ0KcGFja2FnZXMoInRpZHl2ZXJzZSIsICJ0aWR5cXVhbnQiLCAibHVicmlkYXRlIiwgInBhdGNod29yayIsICJmcHAyIiwiZnBwMyIsInNjYWxlcyIsICJ0aW1ldGsiKQ0KDQp0cmFuc2FjY2lvbmVzX21lbnN1YWxlcyA8LSB0cV9nZXQoIkdPT0ciLCBnZXQgPSAic3RvY2sucHJpY2VzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tID0gIjIwMTUtMDEtMDEiKSAlPiUNCiAgc3VtbWFyaXNlX2J5X3RpbWUoDQogICAgLmRhdGVfdmFyICAgICAgPSAgZGF0ZSwgDQogICAgLmJ5ICAgICAgICAgICAgPSAibW9udGgiLA0KICAgIG1vbnRobHlfdm9sdW1lID0gc3VtKHZvbHVtZSksDQogICAgdHJhZGluZ19kYXlzICAgPSBuKCksDQogICAgbWVhbl92b2x1bWUgICAgPSBtZWFuKHZvbHVtZSkpICU+JQ0KICBtdXRhdGUobW9udGggICAgID0geWVhcm1vbnRoKGRhdGUpKSAlPiUgDQogIHNlbGVjdChtb250aCwgZXZlcnl0aGluZygpLCAtZGF0ZSkgJT4lIA0KICBhc190c2liYmxlKGluZGV4ID0gbW9udGgpDQoNCnRyYW5zYWNjaW9uZXNfbWVuc3VhbGVzDQoNCiMgZW4gdmV6IGRlICJzdW1tYXJpc2VfYnlfdGltZSIgc2UgcHVkbyBoYWJlciBoZWNobyBlc3RvOg0KDQojIGdyb3VwX2J5KG1vbnRoID0gZmxvb3JfZGF0ZShkYXRlLCAibW9udGgiKSkgJT4lDQojICAgc3VtbWFyaXNlKG1vbnRobHlfdm9sdW1lID0gc3VtKHZvbHVtZSksDQojICAgICAgICAgICAgIHRyYWRpbmdfZGF5cyA9IG4oKSwNCiMgICAgICAgICAgICAgbWVhbl92b2x1bWUgPSBtZWFuKHZvbHVtZSkpICU+JQ0KIyAgIG11dGF0ZShtb250aCA9IHllYXJtb250aChkYXRlKSkgJT4lIHNlbGVjdChtb250aCwtZGF0ZSwgbW9udGhseV92b2x1bWU6bWVhbl92b2x1bWUpICU+JSANCiMgICBhc190c2liYmxlKGluZGV4ID0gbW9udGgpDQpgYGANClBvZGVtb3MgZ3JhZmljYXIgYW1iYXMgY29uIGRvcyAobyBtw6FzKSBhbHRlcm5hdGl2YXM6IGNhZGEgdmFyaWFibGUgcG9yIHNlcGFyYWRvIHkgdW5pcmxhcyBjb24gYHBhdGNod29ya2AsIG8gdXRpbGl6YXIgYHBpdm90X2xvbmdlcigpYCB5IGBmYWNldF93cmFwKClgLg0KDQpgYGB7cn0NCiMgT3BjacOzbiAxDQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IHRyYW5zYWNjaW9uZXNfbWVuc3VhbGVzKSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSBtb250aCwgeSA9IG1vbnRobHlfdm9sdW1lKSkgKw0KICB5bGFiKCJWb2wuIHRvdGFsIG1lbnN1YWwiKSArIA0KICB4bGFiKCIiKQ0KDQpwMiA8LSBnZ3Bsb3QoZGF0YSA9IHRyYW5zYWNjaW9uZXNfbWVuc3VhbGVzKSArIA0KICBnZW9tX2xpbmUoYWVzKHggPSBtb250aCwgeSA9IG1lYW5fdm9sdW1lKSkgKw0KICB5bGFiKCJWb2wuIHByb21lZGlvIGRpYXJpbyIpICsNCiAgeGxhYigiIikNCg0KcDEgLyBwMg0KIyBPcGNpw7NuIDINCg0KdHJhbnNhY2Npb25lc19tZW5zdWFsZXMgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGMobW9udGhseV92b2x1bWUsIG1lYW5fdm9sdW1lKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgdmFsdWVzX3RvID0gInZhbG9yIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBtb250aCwgeSA9IHZhbG9yKSkgKw0KICBnZW9tX2xpbmUoKSArIHlsYWIoIlRyYW5zYWNjaW9uZXMiKSArIHhsYWIoIiIpICsNCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpDQpgYGANCg0KYGBge3J9DQpwIDwtIGdsb2JhbF9lY29ub215ICU+JSANCiAgZmlsdGVyKENvdW50cnkgPT0gIk1leGljbyIpICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtYyhDb3VudHJ5OlllYXIpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IFllYXIsIHkgPSB2YWx1ZSkpICsNCiAgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKH4gbmFtZSwgc2NhbGVzID0gImZyZWVfeSIpDQoNCnBsb3RseTo6Z2dwbG90bHkocCkNCmBgYA0KDQoNCkxhIHNlcmllIG9yaWdpbmFsIHRvbWFuZG8gZWwgdG90YWwgZGUgdHJhbnNhY2Npb25lcyBtZW5zdWFsZXMgcHJlc2VudGEgbWF5b3IgdmFyaWFjacOzbiBxdWUgY3VhbmRvIGFqdXN0YW1vcyBsYSBzZXJpZSBwb3IgbG9zIGRpYXMgY2FsZW5kYXJpbyAobGxlZ2FuZG8gYWwgdm9sdW1lbiBwcm9tZWRpbyBkaWFyaW8pLg0KDQoNCiMjIyBBanVzdGVzIHBvYmxhY2lvbmFsZXMNCg0KQ3VhbHF1aWVyIHZhcmlhYmxlIHF1ZSBzZSB2ZSBhZmVjdGFkYSBwb3IgbGEgcG9ibGFjacOzbiwgcHVlZGUgc2VyIGV4cHJlc2FkYSBlbiB0w6lybWlub3MgKnBlciBjw6FwaXRhKi4gU2ksIHAuIGVqLiwgY29uIGxhIHBhbmRlbWlhIGRlbCBDT1ZJRC0xOSwgc2UgcXVpc2llcmEgYW5hbGl6YXIgbGEgY2FudGlkYWQgZGUgbcOpZGljb3MgbGFib3JhbmRvIGVuIE3DqXhpY28geSB2ZXIgc3UgZXZvbHVjacOzbiBoaXN0w7NyaWNhLCB0b21hciDDum5pY2FtZW50ZSBsYSBjYW50aWRhZCBkZSBtw6lkaWNvcyBwb2Ryw61hIHNlciBlbmdhw7Fvc28sIHNpIG5vIHNlIHRvbWEgZW4gY29uc2lkZXJhY2nDs24gZWwgY3JlY2ltaWVudG8gZGUgbGEgcG9ibGFjacOzbi4gQXPDrSwgc2Vyw61hIG3DoXMgcmVjb21lbmRhYmxlIG9ic2VydmFyIGxhIHZhcmlhYmxlIGRlIGNhbnRpZGFkIGRlICptw6lkaWNvcyBwb3IgY2FkYSAxMDAgbWlsIGhhYml0YW50ZXMqLCBwb3IgZWplbXBsbywgcGFyYSB2ZXIgc2kgbGEgY2FudGlkYWQgZGUgbcOpZGljb3MgaGEgYXVtZW50YWRvLCBzZSBoYSBtYW50ZW5pZG8gZXN0YWJsZSBvIGhhIGRpc21pbnVpZG8gYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpPdHJvIGVqZW1wbG8gcXVlIHNlIHB1ZWRlIGFuYWxpemFyIGVzIGVsIGRlbCBQSUIgZGUgbG9zIHBhw61zZXMuIFRvbWVtb3MgdHJlcyBjYXNvczogTcOpeGljbywgQXVzdHJhbGlhIGUgSXNsYW5kaWEuIFNpIGNvbnNpZGVyYW1vcyBlbCBQSUIgcGFyYSB2ZXIgcXXDqSB0YW4gYmllbiBzZSBoYSBjb21wb3J0YWRvIGxhIGVjb25vbcOtYSBkZSBjYWRhIHVubyBkZSBsb3MgcGHDrXNlcywgdmVyw61hbW9zIGVsIHNpZ3VpZW50ZSBlZmVjdG86DQoNCmBgYHtyIGNvdW50cmllcyBnZHB9DQpnbGltcHNlKGdsb2JhbF9lY29ub215KQ0KDQpnZSA8LSBnbG9iYWxfZWNvbm9teSAlPiUgDQogIGZpbHRlcihDb3VudHJ5ID09ICJNZXhpY28iIHwgQ291bnRyeSA9PSAiSWNlbGFuZCIgfCBDb3VudHJ5ID09ICJBdXN0cmFsaWEiKQ0KICAjIGZpbHRlcihDb3VudHJ5ICVpbiUgYygiTWV4aWNvIiwgIkljZWxhbmQiLCAiQXVzdHJhbGlhIikpDQoNCnAzIDwtIGdncGxvdChnZSkgKyBhZXMoeCA9IFllYXIsIHkgPSBHRFAsIGNvbG9yID0gQ291bnRyeSkgKyANCiAgZ2VvbV9saW5lKCkNCiANCnAzDQpgYGANCg0Kwr9SZWFsbWVudGUgY3JlZW4gcXVlIGxhIGVjb25vbcOtYSBkZSBNw6l4aWNvIGVzdMOpIGEgbGEgcGFyIGRlIGxhIGVjb25vbcOtYSBkZSBBdXN0cmFsaWEgZSwgaW5jbHVzbywgbXV5IHN1cGVyaW9yIGEgbGEgZGUgSXNsYW5kaWE/DQoNCkxvIGNpZXJ0byBlcyBxdWUgbm8uIExhIHZhcmlhYmxlIGRlbCBQSUIgbm8gZXN0w6EgY29uc2lkZXJhbmRvIGxhIHBvYmxhY2nDs24gZGUgY2FkYSBwYcOtczsgTcOpeGljbyB0aWVuZSB1bmEgcG9ibGFjacOzbiBtdWNobyBtYXlvciBxdWUgbGEgZGUgbG9zIG90cm9zIGRvcyBwYcOtc2VzOg0KDQpgYGB7ciBjb3VudHJpZXMgcG9wdWxhdGlvbn0NCmdncGxvdChnZSkgKyBhZXMoeCA9IFllYXIsIHkgPSBQb3B1bGF0aW9uLCBjb2xvciA9IENvdW50cnkpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQpBaG9yYSwgc2kgYWp1c3RhbW9zIGxvcyBkYXRvcyBkZWwgUElCIHBhcmEgdG9tYXIgZW4gY3VlbnRhIGxhIHBvYmxhY2nDs24sIG9idGVuZHLDrWFtb3MgZWwgKipQSUIgcGVyIGPDoXBpdGEqKi4NCg0KYGBge3IgY291bnRyaWVzIGdkcCBwZXIgY2FwaXRhfQ0KcDQgPC0gZ2dwbG90KGdlKSArIGFlcyh4ID0gWWVhciwgeSA9IEdEUCAvIFBvcHVsYXRpb24sIGNvbG9yID0gQ291bnRyeSkgKw0KICBnZW9tX2xpbmUoKSArIHlsYWIoIkdEUCBwZXIgY2FwaXRhIikNCg0KcDQNCmBgYA0KDQpBcXXDrSBxdWVkYSBjbGFybyBxdWUgbGEgZWNvbm9tw61hIGRlIEF1c3RyYWxpYSBlIElzbGFuZGlhIGVzIGJhc3RhbnRlIHN1cGVyaW9yIGEgbGEgbWV4aWNhbmEgKGxvIHByb2R1Y2lkbyBlbiBNw6l4aWNvIHBvciBwZXJzb25hIGVzIG11Y2hvIG1lbm9yIHF1ZSBsbyBwcm9kdWNpZG8gcG9yIHBlcnNvbmEgZW4gQXVzdHJhbGlhIGUgSXNsYW5kaWEpLg0KDQojIyMgQWp1c3RlcyBpbmZsYWNpb25hcmlvcw0KDQpMb3MgZGF0b3MgYWZlY3RhZG9zIHBvciBlbCB2YWxvciBkZWwgZGluZXJvIGVuIGVsIHRpZW1wbyBzZSBkZWJlbiBhanVzdGFyIGFudGVzIGRlIG1vZGVsYXJzZS4gRXN0byBzZSBkZWJlIGEgbGEgaW5mbGFjacOzbjogc3VzIGFidWVsb3MgcG9kw61hbiBjb21wcmFyIHVuYSBjb2NhIHkgdW4gZ2Fuc2l0byB0YWwgdmV6IGNvbiBtZW5vcyBkZSAzIHBlc29zLiDCv0N1w6FudG8gZGluZXJvIG5lY2VzaXRhcsOtYW4gaG95IHBhcmEgY29tcHJhciB1bmEgY29jYSB5IHVuIGdhbnNpdG8/IEVudG9uY2VzLCBsYXMgc2VyaWVzIGRlIHRpZW1wbyBmaW5hbmNpZXJhcyBzZSBhanVzdGFuIGEgbGEgaW5mbGFjacOzbiwgeSBzZSBleHByZXNhbiBlbiB0w6lybWlub3MgZGUgYWxndW5hIG1vbmVkYSBjb25zdGFudGUgKGVuIGVsIHRpZW1wbykuIFBvciBlamVtcGxvLCBzZSBwdWVkZSBtZWRpciBlbCB2YWxvciBkZSBsYSB2aXZpZW5kYSBlbiBHdWRhbGFqYXJhLCBjb24gcHJlY2lvcyBjb25zdGFudGVzIGRlIDIwMTAgKGltYWdpbmFuZG8gcXVlIG5vIGh1YmllcmEgY2FtYmlhZG8gZWwgdmFsb3IgZGVsIGRpbmVybyBlbiBlbCB0aWVtcG8pLg0KDQpQYXJhIHJlYWxpemFyIGVsIGFqdXN0ZSBpbmZsYWNpb25hcmlvLCBzZSB0b21hIHVuIMOtbmRpY2UgZGUgcHJlY2lvcy4gU2kgJHpfdCQgZXMgZWwgw61uZGljZSBkZSBwcmVjaW9zIHkgJHlfdCQgZXMgZWwgdmFsb3Igb3JpZ2luYWwgZGUgbGEgdml2aWVuZGEgZW4gZWwgdGllbXBvICR0JCwgZW50b25jZXMgZWwgcHJlY2lvIGRlIGxhIHZpdmllbmRhLCAkeF90JCwgYWp1c3RhZG8gYSB2YWxvciBkZWwgYcOxbyAyMDEwLCAgZXN0YXLDrWEgZGFkbyBwb3I6DQoNCiQkDQp4X3QgPSBcZnJhY3t5X3R9e3pfdH0gKiB6X3syMDEwfQ0KJCQNCkxvcyDDrW5kaWNlcyBkZSBwcmVjaW9zIHNvbiBjb25zdHJ1aWRvcywgZ2VuZXJhbG1lbnRlLCBwb3IgZWwgZ29iaWVybm8sIG8gYWxndW5hIGRlcGVuZGVuY2lhIGRlIGdvYmllcm5vLiBFbiBNw6l4aWNvLCBlbCBtw6FzIGNvbcO6biBlcyBlbCDDjW5kaWNlIE5hY2lvbmFsIGRlIFByZWNpb3MgYWwgQ29uc3VtaWRvciAoSU5QQyksIHF1ZSBlcyBjb25zdHJ1aWRvIHBvciBlbCBJTkVHSS4NCg0KVmVhbW9zIGVsIGNhc28gZGUgbGEgaW5kdXN0cmlhIGRlIHBlcmnDs2RpY29zIHkgbGlicm9zIGltcHJlc29zIGVuIEF1c3RyYWxpYSB5IHN1IGNyZWNpbWllbnRvIG8gZGVjcmVjaW1pZW50byBlbiBlbCB0aWVtcG8uIFRvbWFyZW1vcyBsb3MgZGF0b3MgZGUgYGF1c19yZXRhaWxgIHkgYWp1c3RhcmVtb3MgbGEgaW5mbGFjacOzbiBjb24gZWwgw61uZGljZSBkZSBwcmVjaW9zIGFsIGNvbnN1bWlkb3IsIGBDUElgLCBkZW50cm8gZGUgbGEgdGFibGEgYGdsb2JhbF9lY29ub215YC4NCg0KYGBge3IgaW5mbGF0aW9uIGFkanVzdGVkIHByaW50aW5nIGluZHVzdHJ5LCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0NCnByaW50X3JldGFpbCA8LSBhdXNfcmV0YWlsICU+JQ0KICBmaWx0ZXIoSW5kdXN0cnkgPT0gIk5ld3NwYXBlciBhbmQgYm9vayByZXRhaWxpbmciKSAlPiUNCiAgZ3JvdXBfYnkoSW5kdXN0cnkpICU+JQ0KICBpbmRleF9ieShZZWFyID0geWVhcihNb250aCkpICU+JQ0KICBzdW1tYXJpc2UoVHVybm92ZXIgPSBzdW0oVHVybm92ZXIpKQ0KDQphdXRvcGxvdChwcmludF9yZXRhaWwpDQoNCmF1c19lY29ub215IDwtIGdsb2JhbF9lY29ub215ICU+JQ0KICBmaWx0ZXIoQ29kZSA9PSAiQVVTIikNCiAgIyBmaWx0ZXIoQ291bnRyeSA9PSAiQXVzdHJhbGlhIikNCg0KcHJpbnRfcmV0YWlsICU+JSANCiAgIyB1bmlyIGxhcyB0YWJsYXMgcHJpbnRfcmV0YWlsIHkgYXVzX2Vjb25vbXkgY29uIGJhc2UgZW4NCiAgIyBwcmludF9yZXRhaWwNCiAgbGVmdF9qb2luKGF1c19lY29ub215LCBieSA9ICJZZWFyIikgJT4lDQogICMgY2FsY3VsYW5kbyBsYXMgdmVudGFzIHNpbiBpbmZsYWNpw7NuDQogIG11dGF0ZShBZGp1c3RlZF90dXJub3ZlciA9IFR1cm5vdmVyIC8gQ1BJKSAlPiUNCiAgIw0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyAgICAgICAgICAgID0gYyhUdXJub3ZlciwgQWRqdXN0ZWRfdHVybm92ZXIpLA0KICAgIG5hbWVzX3RvICAgICAgICA9ICJUeXBlIiwNCiAgICB2YWx1ZXNfdG8gICAgICAgPSAiVHVybm92ZXIiLA0KICAgIG5hbWVzX3RyYW5zZm9ybSA9IGxpc3QoVHlwZSA9IGFzX2ZhY3RvcikgDQogICkgJT4lIA0KICAjIGdhdGhlciBlcyBsYSB2ZXJzacOzbiB2aWVqYSBkZSBwaXZvdF9sb25nZXIsIHBvciBsbyB0YW50byB5YQ0KICAjIG5vIHNlIHJlY29taWVuZGEgdXRpbGl6YXINCiAgIyBnYXRoZXIoIlR5cGUiLCAiVHVybm92ZXIiLCBUdXJub3ZlciwgQWRqdXN0ZWRfdHVybm92ZXIsIGZhY3Rvcl9rZXkgPSBUUlVFKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IFR1cm5vdmVyKSkgKw0KICAgIGdlb21fbGluZSgpICsNCiAgICBmYWNldF9ncmlkKHZhcnMoVHlwZSksIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogICAgeGxhYigiWWVhcnMiKSArIHlsYWIoTlVMTCkgKw0KICAgIGdndGl0bGUoIlR1cm5vdmVyIGZvciB0aGUgQXVzdHJhbGlhbiBwcmludCBtZWRpYSBpbmR1c3RyeSIpDQpgYGANCg0KDQoNCkFsIHZlciBsb3MgZGF0b3MgYWp1c3RhZG9zLCBub3MgcGVyY2F0YW1vcyBkZSBxdWUgbGEgdmVudGEgZGUgZXN0b3MgcHJvZHVjdG9zIGltcHJlc29zIGhhIGRlY2HDrWRvIG11Y2hvIG3DoXMgZGUgbG8gcXVlIGxvcyBkYXRvcyBzaW4gYWp1c3RhciBzdWdpZXJlbi4NCg0KIyMjIFRyYW5zZm9ybWFjaW9uZXMgbWF0ZW3DoXRpY2FzDQoNClNpIGxvcyBkYXRvcyBwcmVzZW50YW4gdmFyaWFjacOzbiBxdWUgYXVtZW50YSBvIGRpc21pbnV5ZSBjb24gZWwgbml2ZWwgZGUgbGEgc2VyaWUsIHNlIHB1ZWRlIHN1Z2VyaXIgdW5hIHRyYW5zZm9ybWFjacOzbiBtYXRlbcOhdGljYS4gTGFzICoqdHJhbnNmb3JtYWNpb25lcyBsb2dhcsOtdG1pY2FzKiogc29uIG11eSB1dGlsaXphZGFzLiBVbmEgZGUgbGFzIHJhem9uZXMgZXMgcG9ycXVlIHNvbiBmw6FjaWxtZW50ZSBpbnRlcnByZXRhYmxlczogbG9zIGNhbWJpb3MgZW4gZWwgdmFsb3IgbG9nYXLDrXRtaWNvIHNvbiByZWxhdGl2b3MgKG8gcG9yY2VudHVhbGVzKSwgYSBjYW1iaW9zIGVuIGxhIGVzY2FsYSBvcmlnaW5hbC4gVW5hIGRlc3ZlbnRhamEgZGUgbGEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSwgZXMgcXVlIG5vIHNlIHB1ZWRlIHV0aWxpemFyIGNvbiB2YWxvcmVzIGlndWFsZXMgYSBjZXJvIG8gbmVnYXRpdm9zLg0KDQpSZWNvcmRhbmRvIGNsYXNlcyBhbnRlcmlvcmVzLCBkb25kZSB0ZW7DrWFtb3MgbGFzIHZlbnRhcyB0cmltZXN0cmFsZXMgZGUgSiZKLiBTZSBvYnNlcnZhIHF1ZSBsYSB2YXJpYWNpw7NuIGF1bWVudGEgY29uIGVsIG5pdmVsIGRlIGxhIHNlcmllLiBBc2ltaXNtbywgcGFyZWNlIHRlbmVyIHVuYSBmb3JtYSBleHBvbmVuY2lhbC4NCg0KYGBge3IgamogUSBzYWxlc30NCmRhdGEoIkpvaG5zb25Kb2huc29uIikNCmF1dG9wbG90KEpvaG5zb25Kb2huc29uKSsNCiAgZ2d0aXRsZSgiVmVudGFzIHRyaW1lc3RyYWxlcyBkZSBKJkoiKQ0KYGBgDQoNClBvZGVtb3MgcHJvYmFyIGFwbGljYW5kbyB1bmEgdHJhbnNmb3JtYWNpw7NuIGxvZ2Fyw610bWljYSBhIGxvcyBkYXRvcyBwYXJhIHZlciBzaSBsb2dyYW1vcyBoYWNlciBxdWUgbGEgdmFyaWFjacOzbiBzZWEgdW5pZm9ybWUgYSBsbyBsYXJnbyBkZWwgdGllbXBvLCB5IHZlciBzaSBwb2RlbW9zIGhhY2VyIHF1ZSBwYXJlemNhIHRlbmVyIHVuYSB0ZW5kZW5jaWEgbGluZWFsLg0KDQpgYGB7ciBsb2cgamp9DQphdXRvcGxvdChsb2coSm9obnNvbkpvaG5zb24pKSArDQogIGdndGl0bGUoIkxvZ2FyaXRtbyBkZSBsYXMgdmVudGFzIHRyaW0uIGRlIEomSiIpDQpgYGANCg0KDQoNCk90cm8gdGlwbyBkZSB0cmFuc2Zvcm1hY2lvbmVzIG1hdGVtw6F0aWNhcyBzb24gbGFzICoqdHJhbnNmb3JtYWNpb25lcyBkZSBwb3RlbmNpYSoqLiBQLiBlai4sIHNhY2FyIGxhIHJhw616IGN1YWRyYWRhIG8gY8O6YmljYSBkZSBsb3MgZGF0b3MsIGV0Yy4gRXN0YXMgdHJhbnNmb3JtYWNpb25lcyBzZSBwdWVkZW4gZXNjcmliaXIgY29tbyAkd190ID0geV90XnAkLg0KDQpgYGB7cn0NCmF1dG9wbG90KEpvaG5zb25Kb2huc29uXigxLzMpKSArDQogIGdndGl0bGUoIkxvZ2FyaXRtbyBkZSBsYXMgdmVudGFzIHRyaW0uIGRlIEomSiIpDQpgYGANCg0KDQpVbmEgZmFtaWxpYSBkZSB0cmFuc2Zvcm1hY2lvbmVzIG1hdGVtw6F0aWNhcyBxdWUgaW5jbHV5ZSwgdGFudG8gbG9nYXJpdG1vcywgY29tbyBwb3RlbmNpYXMgc29uIGxhcyAqKnRyYW5zZm9ybWFjaW9uZXMgQm94LUNveCoqLCBxdWUgZGVwZW5kZW4gZGUgdW4gcGFyw6FtZXRybywgJFxsYW1iZGEkIHkgc2UgZGVmaW5lbiBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOg0KDQokJHdfe3R9PVxsZWZ0XHtcYmVnaW57YXJyYXl9e2xsfQ0KXGxvZyBcbGVmdCh5X3t0fVxyaWdodCkgJiBcdGV4dCB7IHNpIH0gXGxhbWJkYT0wIFxcDQpcbGVmdCh5X3t0fV57XGxhbWJkYX0tMVxyaWdodCkgLyBcbGFtYmRhICYgXHRleHQgeyBlbiBvdHJvIGNhc28gfQ0KXGVuZHthcnJheX1ccmlnaHQuJCQNCg0KQXF1w60sIGVsIGxvZ2FyaXRtbyBzaWVtcHJlIGVzIHVuIGxvZ2FyaXRtbyBuYXR1cmFsLiBTaSAkXGxhbWJkYSA9IDAkLCBzZSB1dGlsaXphIHVuIGxvZ2FyaXRtbyBuYXR1cmFsLCBkZSBsbyBjb250cmFyaW8gc2UgdXRpbGl6YSB1bmEgcG90ZW5jaWEsIGNvbiB1biBlc2NhbGFkbyBzaW1wbGUuDQoNClNpICRcbGFtYmRhID0gMSQgZW50b25jZXMgJHdfdCA9IHlfdC0xJCwgbG8gcXVlIHNpZ25pZmljYXLDrWEgcXVlIGxvcyBkYXRvcyBzb2xvIHNlIGRlc3BsYXphcsOtYW4gaGFjaWEgYWJham8sIHNpbiBjYW1iaWFyIGxhIGZvcm1hIGRlIGxhIHNlcmllIGRlIHRpZW1wby4gUGFyYSBjdWFscXVpZXIgb3RybyB2YWxvciBkZSAkXGxhbWJkYSQsIGxhIHNlcmllIGNhbWJpYXLDoSBkZSBmb3JtYS4NCg0KKsK/Q8OzbW8gZXNjb2dlciBlbCB2YWxvciBkZSAkXGxhbWJkYSQgYSB1dGlsaXphcj8qDQoNClVuIGJ1ZW4gdmFsb3IgZGUgJFxsYW1iZGEkIGVzIGFxdWVsIHF1ZSBoYWNlIHF1ZSBlbCB0YW1hw7FvIGRlIGxhIHZhcmlhY2nDs24gZXN0YWNpb25hbCBzZWEgZWwgbWlzbW8gYSBsbyBsYXJnbyBkZSBsYSBzZXJpZSwgcGFyYSBxdWUgZWwgbW9kZWxhZG8geSBwcm9uw7NzdGljbyBzZWEgbcOhcyBzZW5jaWxsby4NCg0KUGFyYSBlamVtcGxpZmljYXIgZXN0bywgdG9tZW1vcyBkYXRvcyBzb2JyZSBsYSBwcm9kdWNjacOzbiBkZSBnYXMgZW4gQXVzdHJhbGlhLg0KDQpgYGB7ciBib3ggY294IHRyYW5zZm9ybX0NCnA1YSA8LSBhdXNfcHJvZHVjdGlvbiAlPiUgYXV0b3Bsb3QoR2FzKSsgDQogIGdndGl0bGUoIlByb2R1Y2Npw7NuIGRlIGdhcyAoZGF0b3MgcmVhbGVzKSIpDQoNCnA1IDwtIGF1c19wcm9kdWN0aW9uICU+JSBhdXRvcGxvdChib3hfY294KEdhcyxsYW1iZGEgPSAtMC41KSkgKyBnZ3RpdGxlKCJCb3gtQ294LCBsYW1iZGEgPSAtMC41IikNCg0KcDYgPC0gYXVzX3Byb2R1Y3Rpb24gJT4lIGF1dG9wbG90KGJveF9jb3goR2FzLGxhbWJkYSA9IDApKSArIGdndGl0bGUoIkJveC1Db3gsIGxhbWJkYSA9IDAgKGxvZykiKQ0KDQpwNyA8LSBhdXNfcHJvZHVjdGlvbiAlPiUgYXV0b3Bsb3QoYm94X2NveChHYXMsbGFtYmRhID0gMC4xKSkgKyBnZ3RpdGxlKCJCb3gtQ294LCBsYW1iZGEgPSAwLjEiKQ0KDQpwOCA8LSBhdXNfcHJvZHVjdGlvbiAlPiUgYXV0b3Bsb3QoYm94X2NveChHYXMsbGFtYmRhID0gMSkpICsgZ2d0aXRsZSgiQm94LUNveCwgbGFtYmRhID0gMSIpDQoNCnA1YQ0KDQoocDUgfCBwNikgLyAocDcgfCBwOCkNCmBgYA0KDQpgYGB7ciBib3gtY294IHNoaW55LCBpbmNsdWRlPUZBTFNFfQ0KIyBzbGlkZXJJbnB1dCgibGFtYmRhIiwNCiMgICBsYWJlbCA9ICJTZWxlY2Npb25hIGVsIHZhbG9yIGRlIGxhbWJkYTogIiwNCiMgICBtaW4gPSAtMSwgbWF4ID0gMiwgdmFsdWUgPSAwLA0KIyAgIHN0ZXAgPSAwLjEsIGFuaW1hdGUgPSBGDQojICkNCiMgDQojIHJlbmRlclBsb3Qoew0KIyAgIGF1c19wcm9kdWN0aW9uICU+JSANCiMgICAgIGF1dG9wbG90KGJveF9jb3goR2FzLGxhbWJkYSA9IGlucHV0JGxhbWJkYSkpICsgZ2d0aXRsZSgiVHJhbnNmb3JtYWNpb25lcyBkZSBCb3gtQ294IikNCiMgfSkNCmBgYA0KDQoNCkxhIGNhcmFjdGVyw61zdGljYSBkZSBgZ3VlcnJlcm9gIG5vcyBheXVkYSBhIHNlbGVjY2lvbmFyIHVuIHZhbG9yIGRlICRcbGFtYmRhJC4gRW4gZXN0ZSBjYXNvLCBlc2NvZ2nDsyB1biAkXGxhbWJkYSA9IDAuMTIkDQoNCmBgYHtyIGJveCBjb3ggZ3VlcnJlcm8gZmVhdHVyZX0NCihsYW1iZGEgPC0gYXVzX3Byb2R1Y3Rpb24gJT4lDQogIGZlYXR1cmVzKEdhcywgZmVhdHVyZXMgPSBndWVycmVybykgJT4lDQogIHB1bGwobGFtYmRhX2d1ZXJyZXJvKSkNCg0KYXVzX3Byb2R1Y3Rpb24gJT4lIGF1dG9wbG90KGJveF9jb3goR2FzLCBsYW1iZGEpKQ0KYGBgDQoNCg0KIyBDb21wb25lbnRlcyBkZSBsYXMgc2VyaWVzIGRlIHRpZW1wbw0KDQpTaSBlbXBsZWFtb3MgdW5hICoqZGVzY29tcG9zaWNpw7NuIGFkaXRpdmEqKiwgc2UgcG9kcsOtYSBleHBsaWNhciBhIG51ZXN0cmEgc2VyaWUgZGUgdGllbXBvLCAkeV90JCBjb21vOg0KDQokJA0KeV90ID0gU190ICsgVF90ICsgUl90DQokJA0KDQpEb25kZSAkU190JCByZXByZXNlbnRhIGVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCwgJFRfdCQgbGEgdGVuZGVuY2lhLWNpY2xvIHkgJFJfdCQgZWwgcmVzaWR1by4gVW5hICoqZGVzY29tcG9zaWNpw7NuIG11bHRpcGxpY2F0aXZhKiogdG9tYXLDrWEgbGEgZm9ybWE6DQoNCiQkDQp5X3QgPSBTX3QgKiBUX3QgKiBSX3QNCiQkDQoNCirCv0N1w6FuZG8gdXRpbGl6YXIgbGEgZGVzY29tcG9zaWNpw7NuIGFkaXRpdmEgeSBjdcOhbmRvIGxhIG11bHRpcGxpY2F0aXZhPyoNCg0KTGEgYWRpdGl2YSBlcyBtZWpvciBjdWFuZG8gbGEgbWFnbml0dWQgZGUgbGEgZmx1Y3R1YWNpw7NuIGVzdGFjaW9uYWwgbm8gY2FtYmlhIGNvbiBlbCBuaXZlbCBkZSBsYSBzZXJpZS4gUG9yIGVsIGNvbnRyYXJpbywgY3VhbmRvIGxhIHZhcmlhY2nDs24gZGVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCBjYW1iaWEgYSBsbyBsYXJnbyBkZWwgdGllbXBvLCBzZSByZWNvbWVuZGFyw61hIHV0aWxpemFyIGxhIG11bHRpcGxpY2F0aXZhLiBFc3RhIMO6bHRpbWEgZXMgbXV5IGNvbcO6biBlbiBzZXJpZXMgZGUgdGllbXBvIGVjb27Ds21pY2FzLg0KDQpVbmEgYWx0ZXJuYXRpdmEgcGFyYSBubyB1dGlsaXphciBsYSBkZXNjb21wb3NpY2nDs24gbXVsdGlwbGljYXRpdmEgZXMgdHJhbnNmb3JtYXIgbG9zIGRhdG9zIHBhcmEgZXZpdGFyIGxhIGZsdWN0dWFjacOzbiBlbiBsYSB2YXJpYWNpw7NuIHkgcG9zdGVyaW9ybWVudGUgYXBsaWNhciBsYSBhZGl0aXZhLiBEZSBoZWNobywgc2kgc2UgYXBsaWNhIHVuYSB0cmFuc2Zvcm1hY2nDs24gbG9nYXLDrXRtaWNhIGEgbG9zIGRhdG9zLCBlcyBlcXVpdmFsZW50ZSBhIHV0aWxpemFyIHVuYSBkZXNjb21wb3NpY2nDs24gbXVsdGlwbGljYXRpdmEsIHBvcnF1ZToNCg0KJCR5X3t0fT1TX3t0fSBcdGltZXMgVF97dH0gXHRpbWVzIFJfe3R9IFxxdWFkIFx0ZXh0IHsgZXMgZXF1aXZhbGVudGUgYSB9IFxxdWFkIFxsb2cgeV97dH09XGxvZyBTX3t0fStcbG9nIFRfe3R9K1xsb2cgUl97dH0kJA0KVmVhbW9zIGVsIGVqZW1wbG8gZGVsIGVtcGxlbyBlbiBlbCBzZWN0b3IgZGUgbGFzIHZlbnRhcyBhbCBtZW51ZGVvIGVuIEVFVVUgZGVzZGUgMTk5MC4NCg0KDQpgYGB7ciByZXRhaWwgZW1wbG95bWVudH0NCnVzX3JldGFpbF9lbXBsb3ltZW50IDwtIHVzX2VtcGxveW1lbnQgJT4lDQogIGZpbHRlcih5ZWFyKE1vbnRoKSA+PSAxOTkwLCBUaXRsZSA9PSAiUmV0YWlsIFRyYWRlIikgJT4lDQogIHNlbGVjdCgtU2VyaWVzX0lEKQ0KDQp1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgYXV0b3Bsb3QoRW1wbG95ZWQpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiUGVyc29ucyAodGhvdXNhbmRzKSIpICsNCiAgZ2d0aXRsZSgiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQ0KYGBgDQpWYW1vcyBhIGxsZXZhciBhIGNhYm8gdW4gdGlwbyBkZSBkZXNjb21wb3NpY2nDs24gbGxhbWFkbyAqKlNUTCoqIChxdWUgYW5hbGl6YXJlbW9zIGNvbiBtYXlvciBkZXRhbGxlIHBvc3Rlcmlvcm1lbnRlKS4NCg0KYGBge3IgU1RMIGRlY29tcCB0Ymx9DQpkY21wIDwtIHVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBtb2RlbChTVEwoRW1wbG95ZWQpKQ0KDQpjb21wb25lbnRzKGRjbXApDQpgYGANCg0KRXN0b3MgY29tYW5kb3MgbGxldmFuIGEgY2FibyBsYSBkZXNjb21wb3NpY2nDs24gZGUgbGEgc2VyaWUsIGNvbW8gc2UgcHVlZGUgdmVyIGVuIGxhIHRhYmxhLiBMYSB0ZW5kZW5jaWEsIGB0cmVuZGAgbXVlc3RyYSBlbCBtb3ZpbWllbnRvIGRlIGxhIHNlcmllLCBzaW4gY29uc2lkZXJhciBsYXMgZmx1Y3R1YWNpb25lcyBlc3RhY2lvbmFsZXMgbmkgZWwgcmVzaWR1by4gUG9kZW1vcyBhbmFsaXphciBsYSB0ZW5kZW5jaWEgZGUgbGEgc2VyaWUgZ3LDoWZpY2FtZW50ZToNCg0KYGBge3IgZW1wbG95bWVudCB0cmVuZH0NCnVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBhdXRvcGxvdChFbXBsb3llZCwgY29sb3I9J2dyYXknKSArDQogIGF1dG9sYXllcihjb21wb25lbnRzKGRjbXApLCB0cmVuZCwgY29sb3I9J3JlZCcpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiUGVyc29ucyAodGhvdXNhbmRzKSIpICsNCiAgZ2d0aXRsZSgiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQ0KYGBgDQoNClBvZGVtb3MgZ3JhZmljYXIgbG9zIHRyZXMgY29tcG9uZW50ZXMgc2ltdWx0w6FuZWFtZW50ZSBjb246DQoNCmBgYHtyIFNUTCBkZWNvbXAgcGxvdH0NCmNvbXBvbmVudHMoZGNtcCkgJT4lIGF1dG9wbG90KCkgKyB4bGFiKCJZZWFyIikNCmBgYA0KDQpMYSBncsOhZmljYSBub3MgaW5kaWNhIHF1ZSBzZSBsbGV2w7MgYSBjYWJvIHVuYSBkZXNjb21wb3NpY2nDs24gU1RMLCBkZSBmb3JtYSBhZGl0aXZhLCB5IG5vcyBncmFmaWNhOg0KDQoxLiBMYSBzZXJpZSBvcmlnaW5hbC4NCjIuIExhIHRlbmRlbmNpYS4NCjMuIEVsIGNvbXBvbmVudGUgZXN0YWNpb25hbC4NCjQuIEVsIHJlc2lkdW8uDQoNClNpIHNlIHN1bWEgY2FkYSB1bm8gZGUgbG9zIGNvbXBvbmVudGVzLCBvYnRlbmVtb3MgbGEgc2VyaWUgb3JpZ2luYWwuDQoNCkxhcyBiYXJyYXMgZ3Jpc2VzIGVuIGVzdGFzIGdyw6FmaWNhcyBpbmRpY2FuIGxhcyBlc2NhbGFzIHJlbGF0aXZhcyBkZSBsb3MgY29tcG9uZW50ZXMuDQoNCiMjIyBEYXRvcyBkZXNlc3RhY2lvbmFsaXphZG9zDQoNCkVuIG9jYXNpb25lcywgbG9zIGRhdG9zIHByZXNlbnRhZG9zIHBvciBlbCBnb2JpZXJubyB1IG90cmEgb3JnYW5pemFjacOzbiBzZSBkaWNlIHF1ZSBlc3TDoW4gKipkZXNlc3RhY2lvbmFsaXphZG9zKiouIEVzdG8gc2lnbmlmaWNhIHF1ZSBsZSBxdWl0YXJvbiBlbCBjb21wb25lbnRlIGVzdGFjaW9uYWwgYSBsYSBzZXJpZS4NClNpIHNlIHF1aXRhLCBsb3MgZGF0b3MgYWhvcmEgZXN0w6FuICJhanVzdGFkb3MgZXN0YWNpb25hbG1lbnRlIi4gUGFyYSBsYSBkZXNjb21wb3NpY2nDs24gYWRpdGl2YSwgbG9zIGRhdG9zIGRlc3RhY2lvbmFsaXphZG9zIGVzdMOhbiBkYWRvcyBwb3IgJHlfdCAtIFNfdCQsIG1pZW50cmFzIHF1ZSBlbiBsYSBtdWx0aXBsaWNhdGl2YSBlc3RhcsOtYW4gZGFkb3MgcG9yICRcZnJhY3t5X3R9e1NfdH0kLiBWZWFtb3MgbG9zIGRhdG9zIGRlbCBlbXBsZW8sIGRlc2VzdGFjaW9uYWxpemFkb3M6DQoNCmBgYHtyIGVtcGxveW1lbnQgc2Vhc29uYWxseSBhZGp1c3RlZH0NCnVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBhdXRvcGxvdChFbXBsb3llZCwgY29sb3I9J2dyYXknKSArDQogIGF1dG9sYXllcihjb21wb25lbnRzKGRjbXApLCBzZWFzb25fYWRqdXN0LCBjb2xvcj0nYmx1ZScpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiUGVyc29ucyAodGhvdXNhbmRzKSIpICsNCiAgZ2d0aXRsZSgiVG90YWwgZW1wbG95bWVudCBpbiBVUyByZXRhaWwiKQ0KYGBgDQoNClNpIGxhIHZhcmlhY2nDs24gZGViaWRvIGEgbGEgZXN0YWNpb25hbGlkYWQgbm8gZXMgZGUgaW50ZXLDqXMgZW4gcGFydGljdWxhciwgbG9zIGRhdG9zIGRlc2VzdGFjaW9uYWxpemFkb3MgcHVlZGVuIHNlciBtdXkgw7p0aWxlcy4gVW4gZWplbXBsbyBjb23Dum4gZXMgZWwgbml2ZWwgZGUgZGVzZW1wbGVvIG8gY3JlY2ltaWVudG8gZWNvbsOzbWljby4gRWwgSU5FR0kgcmVwb3J0YSBsYXMgY2lmcmFzIGRlIG1hbmVyYSBkZXNlc3RhY2lvbmFsaXphZGFzLiBFc3RvIG5vcyBwZXJtaXRlIHZlciBlbCBlc3RhZG8gZGUgbGEgZWNvbm9tw61hLCBzaW4gdG9tYXIgZW4gY3VlbnRhIGZhY3RvcmVzIGVzdGFjaW9uYWxlcy4NCg0KIyBNZWRpYXMgbcOzdmlsZXMNCg0KTGEgZGVzY29tcG9zaWNpw7NuIGRlIHNlcmllcyBkZSB0aWVtcG8gY2zDoXNpY2EgdXRpbGl6YWJhIG1lZGlhcyBtw7N2aWxlcyBwYXJhIGRlZmluaXIgZWwgY29tcG9uZW50ZSBkZSB0ZW5kZW5jaWEuDQoNCkFzw60sIHVuYSBzdWF2aXphY2nDs24gZGUgbWVkaWEgbcOzdmlsIGRlIG9yZGVuICRtJCBlc3RhcsOtYSBkYWRhIHBvcjoNCg0KJCQNClxoYXR7VH1fe3R9PVxmcmFjezF9e219IFxzdW1fe2o9LWt9XntrfSB5X3t0K2p9DQokJA0KZW4gZG9uZGUgJG0gPSAyayArMSQuIEVudG9uY2VzLCBlbCBlc3RpbWFkbyBkZSBkZSBsYSB0ZW5kZW5jaWEgZW4gZWwgcGVyaW9kbyAkdCQsICRcaGF0e1R9X3QkLCBzZSBvYnRpZW5lIGFsIHByb21lZGlhciBsb3MgdmFsb3JlcyBkZSBsYSBzZXJpZSBkZSB0aWVtcG8gZGVudHJvIGRlIGxvcyAkayQgcGVyaW9kb3MgYWxyZWRlZG9yIGRlICR0JC4gQSBlc3RvIHNlIGxlIGxsYW1hIHVuYSBtZWRpYSBtw7N2aWwgZGUgb3JkZW4gJG0kOyAkbSQtKipNQSoqLg0KDQpgYGB7ciBtZXggZXhwb3J0cyBwbG90fQ0KcCA8LSBnbG9iYWxfZWNvbm9teSAlPiUNCiAgZmlsdGVyKENvdW50cnkgPT0gIk1leGljbyIpICU+JQ0KICBhdXRvcGxvdChFeHBvcnRzKSArDQogIHhsYWIoIlllYXIiKSArIHlsYWIoIiUgb2YgR0RQIikgKw0KICBnZ3RpdGxlKCJUb3RhbCBNZXhpY2FuIGV4cG9ydHMiKQ0KDQpwbG90bHk6OmdncGxvdGx5KHApDQpgYGANCkVuIGxhIGdyw6FmaWNhIHRlbmVtb3MgbGFzIGV4cG9ydGFjaW9uZXMgZGUgTcOpeGljbyBkZXNkZSAxOTYwIGEgMjAxNywgY29tbyBwb3JjZW50YWplIGRlbCBQSUIuIFBvZGVtb3MgY2FsY3VsYXIgdW5hIG1lZGlhIG3Ds3ZpbCBkZSBvcmRlbiA1OyBlc3RvIGVzLCBvYnRlbmVyIGVsIHByb21lZGlvIGRlIDUgcGVyaW9kb3MsIHBhcmEgY2FkYSBtb21lbnRvLCAkdCQsIGNvbiBlbCBjZW50cm8gZGUgbGEgInZlbnRhbmEiIGVuICR0JC4gQXPDrSwgZGUgYWN1ZXJkbyBhIGxhIGVjdWFjacOzbiBwcmVzZW50YWRhLCAkayA9IDIkIHkgJG0gPSAyayArMSA9IDUkLg0KDQoNCg0KYGBge3IgNS1NQX0NCm1leF9leHBvcnRzIDwtIGdsb2JhbF9lY29ub215ICU+JQ0KICBmaWx0ZXIoQ291bnRyeSA9PSAiTWV4aWNvIikgJT4lDQogIG11dGF0ZSgNCiAgICBgNS1NQWAgPSBzbGlkZV9kYmwoRXhwb3J0cywgbWVhbiwgLnNpemUgPSA1LCAuYWxpZ24gPSAiY2VudGVyIikNCiAgKQ0KDQpgYGANCg0KUGFyYSB2ZXIgY8OzbW8gc2UgcHJlc2VudGEgZXN0YSBtZWRpYSBtw7N2aWwgZ3LDoWZpY2FtZW50ZSwgcG9kZW1vcyBoYWNlciBsbyBzaWd1aWVudGU6DQoNCmBgYHtyIDUtTUEgcGxvdCwgd2FybmluZz1GQUxTRX0NCmdnIDwtIG1leF9leHBvcnRzICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gRXhwb3J0cykpICsgDQogIGdlb21fbGluZSgpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiRXhwb3J0cyAoJSBvZiBHRFApIikNCiAgDQpnZyArIGdlb21fbGluZShhZXMoeSA9IGA1LU1BYCksIGNvbG9yPSdyZWQnKSArDQogIGdndGl0bGUoIlRvdGFsIE1leGljYW4gZXhwb3J0cyAmIDUtTUEiKQ0KDQojIFNlIGxvZ3JhIGxvIG1pc21vIGNvbiBhdXRvcGxvdCgpIHkgYXV0b2xheWVyKCkNCm1leF9leHBvcnRzICU+JSANCiAgYXV0b3Bsb3QoRXhwb3J0cykgKyANCiAgYXV0b2xheWVyKG1leF9leHBvcnRzLCBgNS1NQWAsIGNvbG9yID0gInJlZCIpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiRXhwb3J0cyAoJSBvZiBHRFApIikgKw0KICBnZ3RpdGxlKCJUb3RhbCBNZXhpY2FuIGV4cG9ydHMgJiA1LU1BIikNCmBgYA0KDQpTZSBwdWVkZSB2ZXIgcXVlIGxhIHRlbmRlbmNpYSBlcyBtdWNobyBtw6FzIHN1YXZlIHF1ZSBsb3MgZGF0b3Mgb3JpZ2luYWxlcywgY2FwdHVyYSBlbCBtb3ZpbWllbnRvIHByaW5jaXBhbCBkZSBsYSBzZXJpZSwgcGVybyBkZWphIGRlIGxhZG8gbGFzIGZsdWN0dWFjaW9uZXMgaW50ZXJtZWRpYXMgbyBtZW5vcmVzLg0KDQpRdcOpIHRhbiBzdWF2ZSBlc3TDqSBsYSBjdXJ2YSByZXN1bHRhbnRlLCBkZXBlbmRlcsOhIGRlbCBvcmRlbiBkZSBsYSBtZWRpYSBtw7N2aWwuDQoNCmBgYHtyIG0tTUEgcGxvdHMsIGZpZy5oZWlnaHQ9OCxmaWcud2lkdGg9MTIsIHdhcm5pbmc9RkFMU0V9DQptZXhfZXhwb3J0cyA8LSBtZXhfZXhwb3J0cyAlPiUNCiAgbXV0YXRlKA0KICAgIGAxLU1BYCA9IHNsaWRlX2RibChFeHBvcnRzLCBtZWFuLCAuc2l6ZSA9IDEsIC5hbGlnbiA9ICJjZW50ZXIiKSwNCiAgICBgMy1NQWAgPSBzbGlkZV9kYmwoRXhwb3J0cywgbWVhbiwgLnNpemUgPSAzLCAuYWxpZ24gPSAiY2VudGVyIiksDQogICAgYDctTUFgID0gc2xpZGVfZGJsKEV4cG9ydHMsIG1lYW4sIC5zaXplID0gNywgLmFsaWduID0gImNlbnRlciIpLA0KICAgIGA5LU1BYCA9IHNsaWRlX2RibChFeHBvcnRzLCBtZWFuLCAuc2l6ZSA9IDksIC5hbGlnbiA9ICJjZW50ZXIiKSwNCiAgICBgMTEtTUFgID0gc2xpZGVfZGJsKEV4cG9ydHMsIG1lYW4sIC5zaXplID0gMTEsIC5hbGlnbiA9ICJjZW50ZXIiKSwNCiAgICBgMTUtTUFgID0gc2xpZGVfZGJsKEV4cG9ydHMsIG1lYW4sIC5zaXplID0gMTUsIC5hbGlnbiA9ICJjZW50ZXIiKSwNCiAgICBgMTctTUFgID0gc2xpZGVfZGJsKEV4cG9ydHMsIG1lYW4sIC5zaXplID0gMTcsIC5hbGlnbiA9ICJjZW50ZXIiKSwNCiAgICBgMjEtTUFgID0gc2xpZGVfZGJsKEV4cG9ydHMsIG1lYW4sIC5zaXplID0gMjEsIC5hbGlnbiA9ICJjZW50ZXIiKQ0KICApDQoNCmdnIDwtIG1leF9leHBvcnRzICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gRXhwb3J0cykpICsgDQogIGdlb21fbGluZSgpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiRXhwb3J0cyAoJSBvZiBHRFApIikNCg0KZzEgPC0gZ2cgKw0KIGdlb21fbGluZShhZXMoeSA9IGAxLU1BYCksIGNvbG9yPSdyZWQnKSArDQogIGdndGl0bGUoIjEtTUEiKQ0KZzMgPC0gZ2cgKw0KIGdlb21fbGluZShhZXMoeSA9IGAzLU1BYCksIGNvbG9yPSdyZWQnKSArDQogIGdndGl0bGUoIjMtTUEiKQ0KZzUgPC0gZ2cgKw0KIGdlb21fbGluZShhZXMoeSA9IGA1LU1BYCksIGNvbG9yPSdyZWQnKSArDQogIGdndGl0bGUoIjUtTUEiKQ0KZzkgPC0gZ2cgKw0KIGdlb21fbGluZShhZXMoeSA9IGA5LU1BYCksIGNvbG9yPSdyZWQnKSArDQogIGdndGl0bGUoIjktTUEiKQ0KZzE1IDwtIGdnICsNCiBnZW9tX2xpbmUoYWVzKHkgPSBgMTUtTUFgKSwgY29sb3I9J3JlZCcpICsNCiAgZ2d0aXRsZSgiMTUtTUEiKQ0KZzIxIDwtIGdnICsNCiBnZW9tX2xpbmUoYWVzKHkgPSBgMjEtTUFgKSwgY29sb3I9J3JlZCcpICsNCiAgZ2d0aXRsZSgiMjEtTUEiKQ0KDQooZzEgfCBnMyB8IGc1KSAvDQogIChnOSB8IGcxNSB8IGcyMSkNCg0KIyBHcmFmaWNhbmRvIGxhcyA2IHNlcmllcyBkZSB1bmEgbWlzbWEgdmV6IHV0aWxpemFuZG8gZmFjZXRhcw0KbWV4X2V4cG9ydHMgJT4lIA0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyAgICAgID0gYDUtTUFgOmAyMS1NQWAsDQogICAgbmFtZXNfdG8gID0gIk9yZGVuIiwNCiAgICB2YWx1ZXNfdG8gPSAiTWVkaWEgbcOzdmlsIg0KICApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gWWVhciwgeSA9IEV4cG9ydHMpKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBnZW9tX2xpbmUoYWVzKHkgPSBgTWVkaWEgbcOzdmlsYCksIGNvbG9yID0gInJlZCIpICsNCiAgeGxhYigiWWVhciIpICsgeWxhYigiRXhwb3J0cyAoJSBvZiBHRFApIikgKw0KICBmYWNldF93cmFwKH4gT3JkZW4pDQpgYGANCg0KQ29tbyBzZSBwdWVkZSBvYnNlcnZhciwgdW4gbW9kZWxvICoqMS1NQSoqIHJlYWxtZW50ZSBubyBsbGV2YXLDrWEgYSBjYWJvIG5pbmd1bmEgc3Vhdml6YWNpw7NuLCB5IHVuIG1vZGVsbyAqKjIxLU1BKiosIGVuIGVzdGUgY2Fzbywgc2UgY29udmVydGlyw61hIHByw6FjdGljYW1lbnRlIGVuIHVuYSBsw61uZWEgcmVjdGEuDQoNCiMjIyBNZWRpYXMgbcOzdmlsZXMgZGUgbWVkaWFzIG3Ds3ZpbGVzDQoNCkEgdW5hIHN1YXZpemFjacOzbiBkZSBtZWRpYSBtw7N2aWwgc2UgbGUgcHVlZGUgYXBsaWNhciB1bmEgbnVldmEgc3Vhdml6YWNpw7NuIGRlIG1lZGlhIG3Ds3ZpbC4gUG9yIGVqZW1wbG8sIGNvbnNpZGVyYW5kbyBsYSBwcm9kdWNjacOzbiBkZSAgY2VydmV6YSwgcG9kZW1vcyBvYnRlbmVyIHVuYSBtZWRpYSBtw7N2aWwgZGUgb3JkZW4gNCB5IGEgZXNvIHNhY2FybGUgbGEgbWVkaWEgbcOzdmlsIGRlIG9yZGVuIDI6DQoNCmBgYHtyIE1BIG9mIE1BfQ0KYmVlciA8LSBhdXNfcHJvZHVjdGlvbiAlPiUNCiAgZmlsdGVyKHllYXIoUXVhcnRlcikgPj0gMTk5MikgJT4lDQogIHNlbGVjdChRdWFydGVyLCBCZWVyKQ0KDQpiZWVyX21hIDwtIGJlZXIgJT4lDQogIG11dGF0ZSgNCiAgICBgNC1NQWAgPSBzbGlkZV9kYmwoQmVlciwgbWVhbiwgLnNpemUgPSA0LCAuYWxpZ24gPSAiY2VudGVyLWxlZnQiKSwNCiAgICBgMng0LU1BYCA9IHNsaWRlX2RibChgNC1NQWAsIG1lYW4sIC5zaXplID0gMiwgLmFsaWduID0gImNlbnRlci1yaWdodCIpDQogICkNCg0KYmVlcl9tYQ0KYGBgDQoNCkFsIG9idGVuZXIgbGEgbWVkaWEgbcOzdmlsIGRlIG9yZGVuIDQsICoqNC1NQSoqLCBsbyBxdWUgZXN0YW1vcyBoYWNpZW5kbyBlczoNCg0KJCQNClx0ZXh0ezQtTUF9ID0gXGhhdHtUfV90ID0gXGZyYWN7MX17NH1cbGVmdCh5X3t0LTJ9K3lfe3QtMX0reV97dH0reV97dCsxfVxyaWdodCkNCiQkDQp5LCBhbCBzYWNhciBsYSBtZWRpYSBtw7N2aWwgZGUgb3JkZW4gMiBkZSBlc3RhIG1lZGlhIG3Ds3ZpbCwgKioyICRcdGltZXMkIDQgLSBNQSoqLCBzZXLDrWE6DQoNCiQkDQoyIFx0aW1lcyA0IFx0ZXh0ey1NQX0gPSBcaGF0e1R9X3t0fSA9IFxmcmFjezF9ezJ9XGxlZnRbXGZyYWN7MX17NH1cbGVmdCh5X3t0LTJ9K3lfe3QtMX0reV97dH0reV97dCsxfVxyaWdodCkrXGZyYWN7MX17NH1cbGVmdCh5X3t0LTF9K3lfe3R9K3lfe3QrMX0reV97dCsyfVxyaWdodClccmlnaHRdDQokJA0KU2ltcGxpZmljYW5kbywgcXVlZGFyw61hOg0KDQokJA0KMiBcdGltZXMgNCBcdGV4dHstTUF9ID0gXGhhdHtUfV97dH0gPSBcZnJhY3sxfXs4fSB5X3t0LTJ9K1xmcmFjezF9ezR9IHlfe3QtMX0rXGZyYWN7MX17NH0geV97dH0rXGZyYWN7MX17NH0geV97dCsxfStcZnJhY3sxfXs4fSB5X3t0KzJ9DQokJA0KQXPDrSwgbGxlZ2Ftb3MgYSB2ZXIgcXVlIGxhIG1lZGlhIG3Ds3ZpbCBkZSB1bmEgbWVkaWEgbcOzdmlsIGVzIHNpbXBsZW1lbnRlIHVuYSAqKm1lZGlhIG3Ds3ZpbCBwb25kZXJhZGEqKi4NCg0KIyMjIE1lZGlhcyBtw7N2aWxlcyBwb25kZXJhZGFzDQoNCkNvbW8gYWNhYmFtb3MgZGUgdmVyLCBsYSBjb21iaW5hY2nDs24gZGUgZG9zIG8gbcOhcyBtZWRpYXMgbcOzdmlsZXMgcHJvZHVjZSB1bmEgbWVkaWEgbcOzdmlsIHBvbmRlcmFkYS4gRXN0byBlcywgdW5hIG1lZGlhIG3Ds3ZpbCBxdWUgZGVwZW5kZSBlbiBjaWVydGEgcHJvcG9yY2nDs24gZGUgY2FkYSByZXphZ28uDQoNClVuYSBtZWRpYSBtw7N2aWwgcG9uZGVyYWRhIGRlIG9yZGVuICRtJCBzZSBwdWVkZSBlc2NyaWJpciBjb21vOg0KDQokJA0KXGhhdHtUfV97dH0gPSBcc3VtX3tqPS1rfV57a30gYV97an0geV97dCtqfQ0KJCQNCmRvbmRlICRrID0gKG0gLSAxKSAvIDIkIHkgbG9zIHBlc29zIG8gcG9uZGVyYWNpb25lcyBlc3TDoW4gZGFkb3MgcG9yICRcbGVmdFthX3sta30sIFxkb3RzLCBhX3trfVxyaWdodF0kLiBMYSBzdW1hIGRlIGxvcyBwZXNvcyBkZWJlIHN1bWFyIDEuDQoNClBvZGVtb3MgZGVjaXIsIGVudG9uY2VzLCBxdWUgZWwgY2FzbyBkZSBsYSBtZWRpYSBtw7N2aWwgc2ltcGxlICRtJC1NQSBlcyB1biBjYXNvIHBhcnRpY3VsYXIgZGUgbGEgbWVkaWEgbcOzdmlsIHBvbmRlcmFkYSwgZG9uZGUgdG9kb3Mgc3VzIHBlc29zIHNvbiBpZ3VhbGVzIGEgJDEgLyBtJC4NCg0KYGBge3IgMl8xMk1BLCBldmFsPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQp1c19yZXRhaWxfZW1wbG95bWVudF9tYSA8LSB1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbXV0YXRlKA0KICAgIGAxMi1NQWAgPSBzbGlkZV9kYmwoRW1wbG95ZWQsIG1lYW4sIC5zaXplID0gMTIsIC5hbGlnbiA9ICJjciIpLA0KICAgIGAyeDEyLU1BYCA9IHNsaWRlX2RibChgMTItTUFgLCBtZWFuLCAuc2l6ZSA9IDIsIC5hbGlnbiA9ICJjbCIpDQogICkNCg0KdXNfcmV0YWlsX2VtcGxveW1lbnRfbWEgJT4lDQogIGF1dG9wbG90KEVtcGxveWVkLCBjb2xvcj0nZ3JheScpICsNCiAgYXV0b2xheWVyKHVzX3JldGFpbF9lbXBsb3ltZW50X21hLCB2YXJzKGAyeDEyLU1BYCksIGNvbG9yPSdyZWQnKSArDQogIHhsYWIoIlllYXIiKSArIHlsYWIoIlBlcnNvbnMgKHRob3VzYW5kcykiKSArDQogIGdndGl0bGUoIlRvdGFsIGVtcGxveW1lbnQgaW4gVVMgcmV0YWlsLCAyeDEyLU1BIikNCg0KYGBgDQoNCiQkDQpUX3QgPSBcZnJhY3sxfXsyfVxsZWZ0KFxmcmFjezF9ezEyfSAoIHlfe3QtNn0gKyAgKSBccmlnaHQpDQokJA0KDQoNCiMgTcOpdG9kb3MgZGUgZGVzY29tcG9zaWNpw7NuDQoNCiMjIyBEZXNjb21wb3NpY2nDs24gY2zDoXNpY2ENCg0KSGF5IGRvcyB0aXBvcyBkZSBkZXNjb21wb3NpY2nDs24gY2zDoXNpY2E6IGFkaXRpdmEgeSBtdWx0aXBsaWNhdGl2YS4gRW4gZXN0ZSB0aXBvIGRlIGRlc2NvbXBvc2ljacOzbiwgc2UgYXN1bWUgcXVlIGVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCBlcyBjb25zdGFudGUgYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpgYGB7ciBjbGFzc2ljYWwgZGVjb21wIC0gYWRkaXRpdmV9DQp1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbW9kZWwoY2xhc3NpY2FsX2RlY29tcG9zaXRpb24oRW1wbG95ZWQsIHR5cGUgPSAiYWRkaXRpdmUiKSkgJT4lDQogIGNvbXBvbmVudHMoKSAlPiUNCiAgYXV0b3Bsb3QoKSArIHhsYWIoIlllYXIiKSArDQogIGdndGl0bGUoIkNsYXNzaWNhbCBhZGRpdGl2ZSBkZWNvbXBvc2l0aW9uIG9mIHRvdGFsIFVTIHJldGFpbCBlbXBsb3ltZW50IikNCmBgYA0KDQpIb3kgZW4gZMOtYSwgeWEgbm8gc2UgcmVjb21pZW5kYSB1dGlsaXphciBlbCBtw6l0b2RvIGNsw6FzaWNvIGRlIGRlc2NvbXBvc2ljacOzbiwgeWEgcXVlIGV4aXN0ZW4gZGl2ZXJzb3MgbcOpdG9kb3MgbWVqb3Jlcy4NCg0KQWxndW5hcyBkZSBsYXMgZGVzdmVudGFqYXMgZGVsIG3DqXRvZG8gY2zDoXNpY28gc29uIGxhcyBzaWd1aWVudGVzOg0KDQoqIExhIGVzdGltYWNpw7NuIGRlbCBjb21wb25lbnRlIGRlIHRlbmRlbmNpYSBubyBlc3TDoSBkaXNwb25pYmxlIHBhcmEgbG9zIHByaW1lcmFzIHkgw7psdGltYXMgb2JzZXJ2YWNpb25lcy4gDQoNCiogRWwgY29tcG9uZW50ZSBkZSB0ZW5kZW5jaWEgc3VlbGUgc3Vhdml6YXIgZGUgbcOhcyBpbmNyZW1lbnRvcyBvIGNhw61kYXMgcsOhcGlkYXMgZW4gbG9zIGRhdG9zLg0KDQoqIEFzdW1lIHF1ZSBlbCBjb21wb25lbnRlIGVzdGFjaW9uYWwgc2UgcmVwaXRlIGHDsW8gY29uIGHDsW8sIHBvciBsbyBxdWUgbm8gY2FwdHVyYSBjYW1iaW9zIGVuIGVsIHBhdHLDs24gZXN0YWNpb25hbC4NCg0KIyMjIERlc2NvbXBvc2ljacOzbiBYMTENCg0KRXN0ZSBtw6l0b2RvIGZ1bmNpb25hIGJhc3RhbnRlIGJpZW4gY29uIGRhdG9zIHRyaW1lc3RyYWxlcyB5IG1lbnN1YWxlcy4gRXN0w6EgYmFzYWRvIGVuIGxhIGRlc2NvbXBvc2ljacOzbiBjbMOhc2ljYSwgcGVybyBpbmNsdXllIHBhc29zIGFkaWNpb25hbGVzIHBhcmEgbGlkaWFyIGNvbiBsb3MgcHJvYmxlbWFzIGRlIGVsbGEuDQoNCkFzw60sIFgxMSBsb2dyYSBvYnRlbmVyIGVzdGltYWRvcmVzIHBhcmEgdG9kb3MgbG9zIHB1bnRvcyB5IGVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCBwdWVkZSB2YXJpYXIgbGlnZXJhbWVudGUgY29uIGVsIHRpZW1wby4gVGFtYmnDqW4sIGN1ZW50YSBjb24gbWVjYW5pc21vcyBwYXJhIGxpZGlhciBjb24gdmFyaWFjaW9uZXMgcG9yIGTDrWFzIGNhbGVuZGFyaW8sIGVmZWN0b3MgZGUgZMOtYXMgZmVyaWFkb3MsIGV0Yy4NCg0KYGBge3IgWDExIGRlY29tcH0NCngxMV9kY21wIDwtIHVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBtb2RlbCh4MTEgPSBmZWFzdHM6OjpYMTEoRW1wbG95ZWQsIHR5cGUgPSAiYWRkaXRpdmUiKSkgJT4lDQogIGNvbXBvbmVudHMoKQ0KDQphdXRvcGxvdCh4MTFfZGNtcCkgKyB4bGFiKCJZZWFyIikgKw0KICBnZ3RpdGxlKCJBZGRpdGl2ZSBYMTEgZGVjb21wb3NpdGlvbiBvZiBVUyByZXRhaWwgZW1wbG95bWVudCBpbiB0aGUgVVMiKQ0KYGBgDQpQb2RlbW9zIHV0aWxpemFyIGdyw6FmaWNhcyBlc3RhY2lvbmFsZXMgbyBncsOhZmljYXMgZGUgc3ViLXNlcmllcyBlc3RhY2lvbmFsZXMgcGFyYSB2aXN1YWxpemFyIGxhIHZhcmlhY2nDs24gZW4gZWwgY29tcG9uZW50ZSBlc3RhY2lvbmFsIGEgbG8gbGFyZ28gZGVsIHRpZW1wby4NCg0KYGBge3IgWDExIHNlYXNvbiAmIHN1YnNlcmllcyBwbG90fQ0KeDExX2RjbXAgJT4lIA0KICBnZ19zZWFzb24oKQ0KDQp4MTFfZGNtcCAlPiUgDQogIGdnX3N1YnNlcmllcyhzZWFzb25hbCkNCmBgYA0KDQoNCiMjIyBEZXNjb21wb3NpY2nDs24gU0VBVFMNCg0KIlNFQVRTIiBzb24gbGFzIHNpZ2xhcyBkZSAiU2Vhc29uYWwgRXh0cmFjdGlvbiBBcmltYSBUaW1lIFNlcmllcyIuIEVzdGUgbcOpdG9kbyBzb2xvIGZ1bmNpb25hIHBhcmEgZGF0b3MgdHJpbWVzdHJhbGVzIG8gbWVuc3VhbGVzLiBQb3IgbG8gcXVlLCBzaSBzZSBjdWVudGEgY29uIGRhdG9zIGRlIG90cmEgcGVyaW9kaWNpZGFkLCBzZSBkZWJlIGltcGxlbWVudGFyIG90cm8gbcOpdG9kby4gDQoNCmBgYHtyIFNFQVRTIGRlY29tcH0NCnNlYXRzX2RjbXAgPC0gdXNfcmV0YWlsX2VtcGxveW1lbnQgJT4lDQogIG1vZGVsKHNlYXRzID0gZmVhc3RzOjo6U0VBVFMoRW1wbG95ZWQpKSAlPiUNCiAgY29tcG9uZW50cygpDQphdXRvcGxvdChzZWF0c19kY21wKSArIHhsYWIoIlllYXIiKSArDQogIGdndGl0bGUoIlNFQVRTIGRlY29tcG9zaXRpb24gb2YgdG90YWwgVVMgcmV0YWlsIGVtcGxveW1lbnQiKQ0KYGBgDQoNCiMjIyBEZXNjb21wb3NpY2nDs24gU1RMDQoNClNUTCBzaWduaWZpY2EgIlNlYXNvbmFsIGFuZCBUcmVuZCBkZWNvbXBvc2l0aW9uIHVzaW5nIExvZXNzIiAoIkxvZXNzIiBlcyB1biBtw6l0b2RvIGRlIGVzdGltYWNpw7NuIGRlIHJlbGFjaW9uZXMgbm8gbGluZWFsZXMpLg0KDQpTVEwgdGllbmUgdmFyaWFzIHZlbnRhamFzIHNvYnJlIGxvcyBvdHJvcyBtw6l0b2RvczoNCg0KKiBQdWVkZSB0cmF0YXIgY29uIGN1YWxxdWllciB0aXBvIGRlIGVzdGFjaW9uYWxpZGFkLCBubyBzb2xvIG1lbnN1YWwgbyB0cmltZXN0cmFsLg0KDQoqIEVsIGNvbXBvbmVudGUgZXN0YWNpb25hbCBwdWVkZSB2YXJpYXIgY29uIGVsIHRpZW1wbyB5IGVsIHVzdWFyaW8gZGVjaWRlIGxhIG1hZ25pdHVkIGRlbCBjYW1iaW8uDQoNCiogTGEgc3Vhdml6YWNpw7NuIGRlbCBjb21wb25lbnRlIGRlIHRlbmRlbmNpYSB0YW1iacOpbiBlcyBjb250cm9sYWRvIHBvciBlbCB1c3VhcmlvLg0KDQoqIFB1ZWRlIHNlciByb2J1c3RvIGFudGUgKm91dGxpZXJzKiwgcGFyYSBxdWUgb2JzZXJ2YWNpb25lcyBpbnVzdWFsZXMgbm8gYWZlY3RlbiBlbCBjb21wb25lbnRlIGRlIHRlbmRlbmNpYS4NCg0KTGFzIGRlc3ZlbnRhamFzIGRlIGVzdGUgbcOpdG9kbyBzb24gcXVlIG5vIGNvbnRyb2xhIGRlIG1hbmVyYSBhdXRvbcOhdGljYSBsYSB2YXJpYWNpw7NuIGRlYmlkbyBhIGTDrWFzIGjDoWJpbGVzIG8gdmFyaWFjaW9uZXMgcG9yIGNhbGVuZGFyaW8uIFRhbWJpw6luLCBzb2xvIHBlcm1pdGUgaGFjZXIgZGVzY29tcG9zaWNpb25lcyBhZGl0aXZhcy4NCg0KYGBge3IgU1RMIGRlY29tcH0NCnVzX3JldGFpbF9lbXBsb3ltZW50ICU+JQ0KICBtb2RlbChTVEwoRW1wbG95ZWQgfiB0cmVuZCh3aW5kb3c9NykgKyBzZWFzb24od2luZG93PSdwZXJpb2RpYycpLA0KICAgIHJvYnVzdCA9IFRSVUUpKSAlPiUNCiAgY29tcG9uZW50cygpICU+JQ0KICBhdXRvcGxvdCgpDQoNCiMgbW9kaWZpY2FuZG8gbGEgdGVuZGVuY2lhDQp1c19yZXRhaWxfZW1wbG95bWVudCAlPiUNCiAgbW9kZWwoU1RMKEVtcGxveWVkIH4gdHJlbmQod2luZG93PTE1KSArIHNlYXNvbih3aW5kb3c9J3BlcmlvZGljJyksDQogICAgcm9idXN0ID0gVFJVRSkpICU+JQ0KICBjb21wb25lbnRzKCkgJT4lDQogIGF1dG9wbG90KCkNCg0KIyBtb2RpZmljYW5kbyBsYSBlc3RhY2lvbmFsaWRhZA0KdXNfcmV0YWlsX2VtcGxveW1lbnQgJT4lDQogIG1vZGVsKFNUTChFbXBsb3llZCB+IHRyZW5kKHdpbmRvdz03KSArIHNlYXNvbih3aW5kb3c9MjEpLA0KICAgIHJvYnVzdCA9IFRSVUUpKSAlPiUNCiAgY29tcG9uZW50cygpICU+JQ0KICBhdXRvcGxvdCgpDQpgYGANCg0KRXN0YSBncsOhZmljYSBtdWVzdHJhIHVuYSBkZXNjb21wb3NpY2nDs24gbWVkaWFudGUgU1RMLCBhanVzdGFuZG8gYWxndW5vcyBwYXLDoW1ldHJvcyAoZWwgY29tcG9uZW50ZSBkZSB0ZW5kZW5jaWEgZXMgbcOhcyBmbGV4aWJsZSwgZWwgY29tcG9uZW50ZSBlc3RhY2lvbmFsIGVzIGZpam8geSBzZSBhZ3JlZ8OzIGxhIG9wY2nDs24gZGUgcm9idXN0ZXopLiBDb21vIG11ZXN0cmEgZWwgY8OzZGlnbywgbG9zIGRvcyBwYXLDoW1ldHJvcyBwcmluY2lwYWxlcyBhIHNlbGVjY2lvbmFyIGFsIHVzYXIgU1RMIHNvbiBsYSB0ZW5kZW5jaWEsIGB0cmVuZCh3aW5kb3cgPSB4KWAgeSBsYSBlc3RhY2lvbmFsaWRhZCwgYHNlYXNvbih3aW5kb3cgPSB5KWAuIA0KDQpFc3RvcyBwYXLDoW1ldHJvcyBjb250cm9sYW4gcXXDqSB0YW4gcsOhcGlkbyBjYW1iaWFuIGxvcyBjb21wb25lbnRlcyBkZSB0ZW5kZW5jaWEgeSBlc3RhY2lvbmFsLCByZXNwZWN0aXZhbWVudGUuIFZhbG9yZXMgbcOhcyBiYWpvcyBwcm92b2NhbiBjYW1iaW9zIG3DoXMgcsOhcGlkb3MuICoqTk9UQToqKiBsb3MgdmFsb3JlcyBlc2NvZ2lkb3MgZGUgbG9zIHBhcsOhbWV0cm9zIGRlYmVuIHNlciBpbXBhcmVzLiBTaSBzZSBkZXNlYSBtYW50ZW5lciBlbCBtaXNtbyBjb21wb25lbnRlIGVzdGFjaW9uYWwgYSBsbyBsYXJnbyBkZWwgdGllbXBvLCBzZSBkZWJlcsOtYSBkZWZpbmlyIGNvbW8gcGVyacOzZGljbywgYHNlYXNvbih3aW5kb3cgPSAicGVyaW9kaWMiKWAsIGNvbW8gZW4gZWwgY2FzbyBhbnRlcmlvci4NCg0KIyBUYXJlYQ0KDQoxLiBUb21hbmRvIGVsIFBJQiBkZSBjYWRhIHBhw61zLCBgR0RQYCwgY29udGVuaWRvIGVuIGxhIHRhYmxhIGBnbG9iYWxfZWNvbm9teWAsIGdyYWZpcXVlIGVsIFBJQiBwZXIgY8OhcGl0YSBhIGxvIGxhcmdvIGRlbCB0aWVtcG8uIMK/Q8OzbW8gaGEgc2lkbyBsYSBldm9sdWNpw7NuIGRlIGxhIGVjb25vbcOtYSBkZSBsb3MgcGHDrXNlcyBlbiBlbCB0aWVtcG8/IMK/Q3XDoWwgcGHDrXMgdGllbmUgZWwgbWF5b3IgUElCIHBlciBjw6FwaXRhPw0KDQoyLiBHcmFmaXF1ZSBsYXMgc2lndWllbnRlcyBzZXJpZXMgZGUgdGllbXBvIHkgdHJhbnNmw7NybWVsYXMgeS9vIGFqw7pzdGVsYXMgc2kgbG8gY29uc2lkZXJhIG5lY2VzYXJpby4gwr9RdcOpIGVmZWN0byB0dXZvIGxhIHRyYW5zZm9ybWFjacOzbj8NCiAgDQogICAgaSkgUElCIGRlIEVFVVUsIGRlIGBnbG9iYWxfZWNvbm9teWAuDQogICAgaWkpIFBJQiBkZSBNw6l4aWNvLCB0YW1iacOpbiBkZSBgZ2xvYmFsX2Vjb25vbXlgLg0KICAgIGlpaSkgRGVtYW5kYSBkZSBlbGVjdHJpY2lkYWQgZW4gZWwgZXN0YWRvIGRlIFZpY3RvcmlhIChBdXN0cmFsaWEpLCBkZSBgdmljX2VsZWNgLg0KDQozLiDCv0VzIMO6dGlsIHJlYWxpemFyIHVuYSB0cmFuc2Zvcm1hY2nDs24gZGUgQm94LUNveCBhIGxvcyBkYXRvcyBgY2FuYWRpYW5fZ2FzYD8gwr9Qb3IgcXXDqSBzw60gbyBwb3IgcXXDqSBubz8NCg0KNC4gRWwgZGF0YXNldCBgZm1hOjpwbGFzdGljc2AgdGllbmUgaW5mb3JtYWNpw7NuIGRlIGxhcyB2ZW50YXMgbWVuc3VhbGVzIChtZWRpZGFzIGVuIG1pbGVzKSBkZWwgcHJvZHVjdG8gQSBwYXJhIHVuIHByb2R1Y3RvciBkZSBwbMOhc3RpY29zLCBhIGxvIGxhcmdvIGRlIGNpbmNvIGHDsW9zLg0KICAgIA0KICAgIGkpIEdyYWZpcXVlIGxhIHNlcmllIGRlIHRpZW1wbyBwYXJhIGVsIHByb2R1Y3RvIEEuIMK/SWRlbnRpZmljYSBhbGfDum4gY29tcG9uZW50ZSBkZSB0ZW5kZW5jaWEtY2ljbG8geS9vIGVzdGFjaW9uYWw/DQogICAgaWkpIFV0aWxpY2UgdW5hIGRlc2NvbXBvc2ljacOzbiBjbMOhc2ljYSBtdWx0aXBsaWNhdGl2YSBwYXJhIGNhbGN1bGFyIGVsIGNvbXBvbmVudGUgZGUgdGVuZGVuY2lhIHkgZXN0YWNpb25hbC4NCiAgICBpaWkpIMK/TG9zIHJlc3VsdGFkb3MgY29pbmNpZGVuIGNvbiBzdSByZXNwdWVzdGEgYWwgaW5jaXNvIGkpPw0KICAgIGl2KSBDYWxjdWxlIHkgZ3JhZmlxdWUgbG9zIGRhdG9zIGRlc2VzdGFjaW9uYWxpemFkb3MuDQogICAgdikgQ2FtYmllLCBtYW51YWxtZW50ZSwgdW5hIG9ic2VydmFjacOzbiBwYXJhIHF1ZSBzZWEgdW4gKm91dGxpZXIqIChwLiBlai4sIHN1bWUgNTAwIGEgdW5hIG9ic2VydmFjacOzbikuIFZ1ZWx2YSBhIGVzdGltYXIgbG9zIGRhdG9zIGRlc2VzdGFjaW9uYWxpemFkb3MuIMK/Q3XDoWwgZnVlIGVsIGVmZWN0byBkZSBlc2Ugb3V0bGllcj8NCiAgICB2aSkgwr9IYWNlIGFsZ3VuYSBkaWZlcmVuY2lhIHF1ZSBlbCBvdXRsaWVyIHNlIGVuY3VlbnRyZSBjZXJjYSBkZWwgZmluYWwgZGUgbGEgc2VyaWUgbyBtw6FzIGFscmVkZWRvciBkZWwgY2VudHJvPw==